Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Routing
  • Hey :)

    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



    Is there a way to achieve this in fuelPHP?

    best regards

    MrMelz



  • 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.

    disclaimer: not tested. ;)
  • Hey Harro,

    thx for your comment :)

    i've gone another way, maybe you can tell me, if this is also a correct fuelphp way :)

    I've overwritten the classes: Route und Router using this:

    \Autoloader::add_classes(array(
        // Add classes you want to override here
         'Router' => APPPATH.'vendor/router.php',
         'Route' => APPPATH.'vendor/route.php'
    ));


    then moved to the local copy of router.php an added the following lines into
    public static function process, after if(!match ), my code

    $uri            = Input::server('PATH_INFO');
    $uri            = str_replace('/', '',  $uri );

    $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();
            }

            $result = $this->_parse_search($uri, null, $method);

            if ($result)
            {
                return $result;
            }

            return false;
        }
        
        
        
    is that also a correct way?

    Disclaimer: tested 2 times ;-)

    Thx!
  • 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)
  • thx for your replay :)

    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.

    I will give  Router::add a try

    big thx!
  • State of the art? Or a necessity?

    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. ;-)
  • Router::add works!
    thx!
  • Cool. You're welcome. ;-)

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion