Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
CMS can not be this easy!
  • Hi, I am looking into a very simple cms built using Fuel but keeping the default Fuel routing happy so allowing for additional modules to be simply dropped in and added to the menu. I must be missing something as this looks too easy... config->routes
    '_root_'  => 'welcome/index',  // The default route
    '_404_'   => 'welcome/index',  //'welcome/404',    // The main 404 route
    

    welcome/index controller
    public function action_index()
        {
            $segments = explode('/', Uri::detect(), 3); // Just break it in to 3 segments for now
    
            //Note $page[0] is empty so ignore it
            $data['page'] = isset($segments[1]) ? $segments[1] : '/';
            $data['parameters'] = isset($segments[2]) ? $segments[2] : null;
    
            // Search the database for the page        
            // My db query code here
            
            // Ignore this testing variable. It would normaly be set in relation to the responce from the db query
            $results = true;
            
            // Do we have any results from the db query
            if($results)
            {
                // Build view with wigets etc...
           }
            else
            {
                Response::redirect(Uri::create("welcome/404", 'refresh'));
            }
            
            // Display the page
            $this->response->body = View::factory('welcome/index', $data);
        }
    

    Is that all there is too it? Phil.
  • Hey Guys, I'm joining this conversation way late (and please correct me if this has been talked about more another place) but I think the way Phil originally proposed it is the way to go with a few corrections. I think there should be another check right before the return false in the Router::find_controller() that checks if __catchall__ is set. If so it passes the uri information through whatever method is there and waits for the $match->action, $match->controller etc to be passed back as an array. If it is not then it jumps straight to 404 like normal. This approach is a bit better IMO because instead of an all or nothing approach it would take controllers, modules, and routes first then as a last resort before 404 it would check whatever method you provided to route for something like a CMS. I'd really like to hear everyone's thoughts on this. I need it for a project and am going to be coding it up anyways so if its seen as a good thing I'll submit a pull request on core.
  • I don't think this is the way to go, as it doesn't fix the real issue, which is: the 404 route is not a controller/method combo, it's a route. This means you can't simply use it as a last resort route, it has to be processed by the routing engine again. Which leads to recursion issues as the 404 route itself can result in a 404 when processed. I think it should be considered a fixed definition (like all righthand sides of route definitions), so you can deal with it as a last-resort setting in the router. This allows you to handle it like any other request, and have all Fuel features working, whether or not it's a 404 method you're calling. It also means that the 404 method should set the response status to 400, and that show_404() simple creates a request object for the controller/method defined in the 404 route. And as it is non-routeable, you can then also prefix it with a module name to access a non-routeable module method. It we take this approach, a lot of the code dealing with the special 404 situation can go.
  • I'm not saying that we use the 404 in my approach. Im talking about a separate check just like we do to see if its a controller thats in a folder, or in a module etc.
    // Code Here for checking modules, controllers & folders
    
    if($route['__catchall__'] != null)
    {
        list($match->directory, $match->controller, $match->method) = call_user_func($route['__catchall__'];
        return $match;
    }
    
    // Code Here for returning false to the request so that it routes to a 404
    

    excuse the obvious wrong variable names i just wanted to further explain quickly
  • My point is that if we deal with the odd way of 404 routing, we don't need to introduce _catchall_ as a workaround, because basically the 404 route IS (or should be) a catch all... The router should deal with the 404 handling internally, and return that route if no other can be found. It should only return false in case no 404 is defined, or the defined controller/method does not exist. In which case a generic Fuel not-found error should be generated. If we take this approach, 404 routes will be processed like all others, so no more issues in your methods due to different processing of the 404 route.
  • I agree we should deal with the odd 404s but if you lump this together with the 404s then you would have to create a new request object to route the catchall the correct controller. It would be a lot cleaner to just return the controler/directory/action to the router for the catchall.
  • If you want something like that you can extend the Route class (note the lack of an "r" at the end) and add an instance of your own Route type to the routes. This will allow you to "catch-all" within that Route object or still return false if you need to, so it might move on to the 404 route.
  • Thats what I planned to do at the beginning but I think this is functionality that many could benefit from, for instance doing http://mysite.com/username type of stuff is pretty common & one of the complaints with CI was there wasn't any good way to do DB based routing without a bunch of work.
  • Any such functionality will require some implementation and overhead. It's uneccessary to add the overhead for everyone, and for implementation you can choose between the 404 method and the Route extension method. I think that's more than enough: we offer the tools, you do the rest.
  • I'll have a go at rewriting the 404 logic when I have the time. The way it works now is not logical, it can be simplified a great deal without breaking anything, and allowing you to use the _404_ route as a catch-all without having to deal with today's oddities...
  • "Only" the admin left ;) Though I'd use the Uri::segments() method instead and a small correction to your redirect:
    public function action_index()
        {
            $segments = Uri::segments();
    
            //Note $page[0] is empty so ignore it
            $data['page'] = isset($segments[1]) ? $segments[1] : '/';
            $data['parameters'] = array_slice($segments, 1);
    
            // Search the database for the page        
            // My db query code here
            
            // Ignore this testing variable. It would normaly be set in relation to the responce from the db query
            $results = true;
            
            // Do we have any results from the db query
            if($results)
            {
                // Build view with wigets etc...
            }
            else
            {
                Response::redirect('welcome/404', 'refresh');
            }
            
            // Display the page
            $this->response->body = View::factory('welcome/index', $data);
        }
    
  • Excellent! I tried $segments = Uri::segments(); but it doesn't work thats why I used Uri::detect() Example url [url=http://fuel/page/param]http://fuel/page/param[/url] If I use Uri::detect() I get:
    $segment[1] = page
    $segment[2] = param but if I use Uri::segments() I get:
    $segment[1] = index
    $segment[2] = nothing it stays empty??? I have not looked at the Fuel class code for Uri yet but will do in a minute but my first thought was and quite rightly:
    If the controller/method in the url is not found or rather matched to a controller (class) Fuel appears to strip them out and drop them. Will go and dig in to the code now :-) Phil.
  • Found out why Uri::segments() does not work. Basically the it is down to the request class firing a 404 because the controller can not be found which is 100% correct. The 404 route is loaded from config->routes which is this case is the same as the default controller welcome/index hence segments[0] = welcome and segments[1] = index. Uri::detect() is just basically returning the raw uri from $_SERVER with additional checking which is what I require to be able to make this work. I just need to ensure that a user does not create a page with the same name as one of my controllers otherwise surprise, surprise the controller would get launched instead or the expected user created page lol... Thanks for your help with this question Jelmer and in confirming that what I was doing was not completely wrong. I just could not believe it could be that simple. Right just the admin section to write :-) Phil.
  • Do not use the 404 as a capture-all, as it's processed differently (as you have found out). Instead, use something like
    '(.*)' => 'welcome/index'
    
  • Hi WanWizard, Maybe this is more in depth than I first thought. Using the 404 to do a catch all makes this work the way I want it to. Right this is what I want to achieve (Flexibility). A customer asks me for a web application and I develop all of the required modules. Fuel processes everything normally routing to the selected controller naturally and launches it. The customer at some later date after the site is live then asks for another section where they can control the page content but wants to keep all of the other modules already written (happens way to often but hey that is the way it is). They just want to bolt on a new controller to basically give them a simple cms. Doing it though the 404 and using it as a catch all ensures Fuel still continues to work as before but with the exception of the case in which it can not find the controller to launch so the request class fires off a 404. The route has been modified to load the welcome or page controller which processes the request and if it too fails it then fires off a real 404. '(.*)' => 'welcome/index' Will route everything through the welcome controller which would be 100% correct if I had been asked for a cms to start with but in this instance it is not what I want. I would then have to start building a router to route to the request class to access the existing classes etc... That would be reinventing the wheel as it already exists. I also did not like the 404 catch all method which was the original reason for the post to make sure this was the correct way to achieve this and not break anything else in the process. Can you think of another way? Thanks for getting me thinking about this a bit more :-) Phil.
  • Actually I don't agree on this with WanWizard. There's nothing wrong with using the 404 catching this way for the exact reasons you pointed out. There's some downsides however: 1. You should be aware that your request is a 404 request and as such things like the Uri class won't contain your Uri like usual.
    2. it takes a little bit more resources as some filesystem checking needs to be done before ending up in the 404 I used to do it this way as well though, the basic idea being the drop-in modules/controllers for added functionality. But I run multiple clients of one installation and they don't all get access, so I ended up with the following ugly hack in CI (I'm slowly moving it to Fuel where I could use a Route extension to do the same): - It uses a catch-anything route like WanWizard suggested at the end
    - But first the config file contains a call to the cache class to see if it has any routes cached, if not a DB call is done to get the routes (=modules/controllers) the customers has access to It works as well, but a little less drop-in and a bit more installation.
  • The whole thing could be resolved by adding another default route how about: Wait for it....
    '_catchall_' => 'catchallcontroller/index'
    

    This would only be active if it is required and un-commented in config->routes and if it exists then circumvent all of the filesystem checks and launches the controller like any other controller.
    '_root_' => 'welcome/index',
    // '_catchall_' => 'catchallcontroller/index',
    '_404_' => 'welcome/404',
    

    If it remains commented out then all stays the same. This would be awesome and offer a super high level of flexibility that no other framework offers to date. Well I thought it was a good idea anyway lol. I am happy with using the 404 re-route but as you pointed out it adds some overhead which is not really the most desirable solution. Phil.
  • What you call "_catchall_" is exactly what WanWizard's suggestion does: it bypasses any filesystem check for a controller and just sends it all to your default controller. The disadvantage of that is that you'll have to register any exception (any controller/module really) before that rule. In other words: this is already possible.
  • Oh yeah lol... (head now in palms of hands) Isn't it funny when you get so wrapped up in something you can't see the obvious anymore. 404 trapping it is then. ;-) Thanks, Phil.
  • My reason for suggesting it was just because 404 is handled differently from a standard route. Which means you can't just take existing code, and use it in a 404 route as a catchall, as the TS discovered. Which you can with my suggestion (a bit Q&D perhaps, but very effective).

Howdy, Stranger!

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

In this Discussion