Dynamic routing

Hello everyone!

I must say, I totally fell in love with Phalcon.

I'm trying to create a basic CMS-like application with a mixture of controller based pages and ones created dynamically. In a sense, that users can create pages as they wish based on html from database and not pre-made controllers / views.

My initial idea was simple - set up a basic route for all known pages and use 404 handler to redirect to the 'dynamic' page generator

$router->add(
        "/:controller/:action/:params",
        array(
            "controller" => 1,
            "action"     => 2,
            "params"     => 3,
        )
    );    
    //Set 404 paths
    $router->notFound(array(
        "controller" => "pages",
        "action" => "dynamic"
    ));

In theory - this was great. But in practice, it doesn't work. For example, test.com/someth/to/test still matches the first route and since the controller doesn't exist, Phalcon shows "SomethController handler class cannot be loaded" error.

Has anyone got some suggestions on how to overcome that? Is there a way to override the default 'controller not found' phase?

Thanks

EDIT Ach, only just found this: http://forum.phalconphp.com/discussion/106/difference-between-setdefaults-and-notfound#C442

If anyone is interested in the solution:

$di->set('dispatcher', function() use ($di) {

    //Obtain the standard eventsManager from the DI
    $eventsManager = $di->getShared('eventsManager');

    //Listen for events produced in the dispatcher using the Security plugin
    $eventsManager->attach('dispatch', $security);

    $eventsManager->attach("dispatch", function($event, $dispatcher, $exception) {

    if ($event->getType() == 'beforeException') {
        switch ($exception->getCode()) {
          case Phalcon\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
          case Phalcon\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
            $dispatcher->forward(array(
              'controller' => 'pages',
              'action' => 'dynamic'
            ));
            return false;
        }
      }
    });

    $dispatcher = new Phalcon\Mvc\Dispatcher();

    //Bind the EventsManager to the Dispatcher
    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
});

Problem solved, unless there is a nicer way of doing it?.



81.2k

You only need this part of code:

<?php

$di->set('dispatcher', function() {

        $eventsManager = new Phalcon\Events\Manager();

        $eventsManager->attach("dispatch", function($event, $dispatcher, $exception) {
            if ($event->getType() == 'beforeException') {
                switch ($exception->getCode()) {
                    case Phalcon\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
                    case Phalcon\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                        $dispatcher->forward(array(
                            'controller' => 'errors',
                            'action' => 'page404'
                        ));
                        return false;
                }
            }
        });

        $dispatcher = new Phalcon\Mvc\Dispatcher();

        //Bind the EventsManager to the Dispatcher
        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
});


8.7k

Thanks for the tip. I use a custom security plugin for user authentications and stuff, which is why I've got the other bits.

@nazwa How did you get custom url that user input at pages controller ?



8.7k
edited Jun '14

Hey! It was actually very simple.

Basically look at my edit above. You need to hook up to the dispatcher events and capture module / action not found. Forward that internally to a specific controller - in my case - pages/dynamic.

Inside dynamic you can check the actual url: $this->router->getRewriteUri() and job done :) You can look this url up in database and display anything you like!

And of course, if url is not found in db, dont forget to throw 404.

Hello @nazwa , I also have using Security plugin. But I have bump to some problem.

When go to any url , your code word perfectly to load dynamic controller , but in security plugin , it seem , it have some problem to detect the right controller name.

Example : my url is http://xxx.com/page , it suppose to load dynamic controller. But , security plugin detect this url controller as "page" , so , it been forward to login controller.

Can you share your security plugin code ? Thanks.

Solved. I have made some adjustment to my security plugin code.



8.7k

I'm glad you solved it! You pretty much need to keep your page controller unsecurred, so everyone can access it, and do some separate url based security checking inside if the custom route has been detected!

edited Sep '14

Hello @nazwa.

I notice that , when using this code , I can't get correct httpreferer.

Example :

Link is http://xxx.com/yyy (It will be dispatch forward to controller Page) Referer suppose to return from facebook.com But , at controller Page , I get httpreferer = http://xxx.com/yyy

I try to get httpreferrer information before dispatch forward code , but still the same. Is there any solution for this ?

Thanks.

Update

@Phalcon , any suggestion regarding this problem ?



6.4k

Please also be noticed that if you have multimodule application you have to add default namespace for the dispatcher. I've done it and my application running in the correct way.

<?php

$di->set('dispatcher', function() {

        $eventsManager = new Phalcon\Events\Manager();

        $eventsManager->attach("dispatch", function($event, $dispatcher, $exception) {
            if ($event->getType() == 'beforeException') {
                switch ($exception->getCode()) {
                    case Phalcon\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
                    case Phalcon\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                        $dispatcher->forward(array(
                            'controller' => 'errors',
                            'action' => 'page404'
                        ));
                        return false;
                }
            }
        });

        $dispatcher = new Phalcon\Mvc\Dispatcher();

        //Bind the EventsManager to the Dispatcher
        $dispatcher->setEventsManager($eventsManager);

        $dispatcher->setDefaultNamespace('APP\\MODULE\\Controllers');

        return $dispatcher;
});