i'm new to fuelphp and i want to realize the following routing:
Currently i'm implementing some kind of a cms in which you can create static pages and dynamic pages. Static pages for example: shipping-costs.html and dynamic pages like red-t-shirt.html
For this i've created a url_rewrite mysql table with the following structure
id|request_path|target_path
this table needs to be checked by the fuelphp router.
example for pages ---- request_path e.g. shipping-costs.html -> url the user is calling target_path e.g. /pages/id/5 -> internal fuelPHP call, to get to the pages controller
example for products ---- request_path e.g. red-t-shirt.html -> url the user is calling target_path e.g. /product/id/5 -> internal fuelPHP call, to get to the product controller
but i will also use de fuelphp default routing mechanism, if no URL was found in de url_rewrite table
What we do in our application framework is use a catch-all route, the last route defined in our route.php:
':any' => 'webengine/loader' // Catch-all
this redirects any requests not routed to our loader controller, which does something dynamic like you want to do. This however disables Fuel's default route -> controller mapping.
You can also reverse the process, and do
'_404_' => 'webengine/loader',
this would call the loader controller if Fuel wasn't able to find a controller for the page requested.
This last option however means you have to be careful with controllers you don't want to be publicly available, so these need a before() method that throws an HttpNotFoundException() if the request is not an HMVC request (but a browser request).
Which brings us to the next problem: HttpNotFoundException() uses the _404_ route above to find which controller method handles your 404's. And with that route, you have potentionally created a call loop.
Last and most flexible option is to use Fuel's HMVC ability.
Use the "any" defined above, at the end of your route.php. This will process all defined routes, and calls the "loader" controller if no defined route was matched.
In your loader controller, you use the current request route to issue a new Request, return the response, capture the HttpNotFoundException, and if that happens, call the method that checks your database, so something like this:
class Loader extends \Controller
{
public function index()
{
try
{
// try the requested URI, but do not process routes.php again!
$request = new \Request::forge(null, false, \Input::method());
$response = $request->execute()->response();
}
catch (\HttpNotFoundException)
{
// check your database here
$customroute = $this->domydatabasecheckhere();
$request = new \Request::forge($customroute, false, \Input::method());
$response = $request->execute()->response();
}
return $response;
}
}
and if your database check doesn't find a match, your check code can throw HttpNotFoundException(), which then gets processed the standard way.
$customRoute = \Model_Urlrewrite::find('first', array('where' => array('request_path' => $uri ) ) ); if( $customRoute !== null ) { /** @var Route $match */ $match = new \Route(preg_quote($customRoute->target_path, '#'), $customRoute->target_path); // add new parse URL function to the Route, see below $match->parse_url_rewrite_request($request, $customRoute->target_path ); } else { // Since we didn't find a match, we will create a new route. $match = new \Route(preg_quote($request->uri->get(), '#'), $request->uri->get()); $match->parse($request);
}
then added parse_url_rewrite_request to the "Route" class. This function only works with the target_path instead the uri
public function parse_url_rewrite_request(\Request $request, $target_path) { $uri = $target_path; $method = $request->get_method();
if ($uri === '' and $this->path === '_root_') { return $this->matched(); }
In general, overrwiting framework code (in the fuel/core or fuel/packages directories) is a very bad idea, as your changes will be erased when you upgrade to a newer version.
If needed, it is better to extend a core class, like you have done it, but that means you have to potentionally do a lot of work when there is a framework update, and your changes are no longer compatible with the new core classes.
If you want a solution without secondary requests, you can also load your database routes, and manually add them to the routing table using Router::add() (see https://fuelphp.com/docs/classes/router.html#/method_add). If you have an any route at the end, make sure you use prepend. You can do this in your app's bootstrap, and use caching to avoid too many database hits.
I personally would choose modifying framework code as an absolute last resort, and if needed, try using your own methods instead of overwriting already defined methods.
We extend:
- Lang: which displays "string missing" in black on yellow if a language string is not defined
- Log: we send our logs to syslog instead of to a file
- Presenter: to define the namespace for presenter classes
- Theme: we added installer code
- View: to be able to load a view from a string (f.e. when stored in a database column)
overwriting core files is more than a bad idea. I've extended them, as described in the manual. But extending core classes is state of the art in nearly every ecommerce framework i've worked with.
If you (have to) modify core classes, you always have to do rework, and do extensive tests to make sure the update hasn't broken your product, whenever you upgrade the framework. Which takes time, and time == money.
So I'd rather avoid it if at all possible, it will make your products better, and more money in the bank. ;-)