We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Route does not work with action and parameter

Hi,

I'd run into a strange problem while I'm trying to set up an error handler controller.

I have an ErrorController:


namespace Main\Controllers;

class ErrorController extends ControllerBase
{

    public function initialize()
    {
        /* some stuff here */
    }

    public function indexAction()
    {
        $code = $this->dispatcher->getParam('code');
        switch($code){
            case 99: $this->flash->error($this->main_text['error99']); break;
            case 404: $this->flash->error($this->main_text['error404']); break;
            default: $this->flash->error($this->main_text['error500']);
        }
    }
}

I have this at the end of my routing:


$router->add('/error/{code:[0-9]+}', array(
    'namespace' => 'Main\\Controllers',
    'controller' => 'error',
    'action' => 'index'
));

When I try to reach f.e. /error/404, I got the Object not found error. Same when I try /error/index, /error/index/404 and Access forbidden for /error/. It works only for /error, then it runs the index action.

I have a forwarding for errors in beforeException event, it also calls this controller and action, but it doesn't work neither for forwarding nor redirecting. I tried to comment this forwarding out, but the result was the same.


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

        switch ($exception->getCode()) {
            case \Phalcon\Mvc\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
            case \Phalcon\Mvc\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
            case \Phalcon\Mvc\Dispatcher::EXCEPTION_INVALID_HANDLER:
            case \Phalcon\Mvc\Dispatcher::EXCEPTION_INVALID_PARAMS:
                $dispatcher->forward(
                    array(
                        'controller' => 'error',
                        'action'     => 'index',
                        'params'     => 404
                    )
                );
                return false;
            default:
                $dispatcher->forward(
                    array(
                        'controller' => 'error',
                        'action'     => 'index',
                        'params'     => 500
                    )
                );
                return false;
        }
    });

I have no idea why the routing doesn't work, all other routing patterns works fine, and I don't find other pattern that may conflict with this one. Do I miss some important point?

I still use Phalcon 2.

edited Sep '16

Your approach seems too complex for a simple task to dispatch errors.

Take a look at my production-proven example. Built on Phalcon 2.0.x.


//Dispatcher classes
use \Phalcon\Dispatcher,
\Phalcon\Mvc\Dispatcher as MvcDispatcher,
\Phalcon\Events\Manager as EventsManager;

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

    // Create an EventsManager
    $eventsManager = new EventsManager();

    //Triggered before executing the controller/action method. At this point the dispatcher has been initialized the controller and know if the action exist.
    /*$eventsManager->attach('dispatch:beforeExecuteRoute', function () {
        //If we ever need to log all matched routes, even notFound routes
     });*/

    // Attach a listener
    $eventsManager->attach('dispatch:beforeException', function ($event, $dispatcher, $exception) {
        // Handle 404 exceptions, this is real 404 not found error, it will trigger 404 http status code
      if ($exception instanceof \Phalcon\Mvc\Dispatcher\Exception) {
          //TODO if we need to get a code and treat it differently depending on the level of exception
          //echo $exception->getCode();
          $dispatcher->forward(['controller' => 'error', 'action' => 'notFound']);

          return false; //returning false is mandatory
      }

    });

    $dispatcher = new MvcDispatcher();

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

    return $dispatcher;
});

My first approach was the same as yours, I actually did the same as you do in this script. But I want to make difference between the errors to display different messages. What is not simply a 404 error, I want to display as 500 (or at least a message that refers to that), and I have one more case what I call 99, that is when someone tries to call an ajax request from URL (I handle that case at the beginning of every ajax called action). I tried to use the same error controller for all the cases, but it seems that my solution is not correct this way.

But what I found strange is why the routing works that way as I wrote. I mean that I have a correct routing and a correctly set up controller and action (or at least I think that they are correct), and it doesn't find the correct handler for that request (when not forwarding, just simply doing an URL request). It works when I call only the controller, but doesn't work when I call it with a parameter. It should not have thrown an exception, while there is a route and handler for the request - but it still does. What may cause it?

edited Sep '16

I rearranged the whole error handling and it doing forwards only to the IndexController's error actions without parameters, and now everything works as expected.

However, I still don't know what exactly was the problem with the previous solution.

All in all, I reached the desired results.

In services at dispatcher:


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

    switch ($exception->getCode()) {
        case \Phalcon\Mvc\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
        case \Phalcon\Mvc\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
        case \Phalcon\Mvc\Dispatcher::EXCEPTION_INVALID_HANDLER:
        case \Phalcon\Mvc\Dispatcher::EXCEPTION_INVALID_PARAMS:
            $dispatcher->forward(
                array(
                    'controller' => 'index',
                     'action'     => 'notFound'
                )
            );
            return false;
        default:
            $dispatcher->forward(
                array(
                    'controller' => 'index',
                    'action'     => 'error'
                )
            );
        return false;
    }

});

In IndexController:


namespace Main\Controllers;

class IndexController extends ControllerBase
{
    public function notFoundAction(){

        $this->loadCustomTrans('error', 'error_text');
        $this->view->pick(["index/error"]);
        $this->view->setVar("code", 404);
        $this->view->setVar("message", $this->error_text['error404']);
    }

    public function errorAction(){

        $this->loadCustomTrans('error', 'error_text');
        $this->view->pick(["index/error"]);
        $this->view->setVar("code", 500);
        $this->view->setVar("message", $this->error_text['error500']);
    }
}

In ControllerBase for ajax actions request tries through URL (now it displays only a flash error message without any forwarding or redirection):


namespace Main\Controllers;

class ControllerBase extends \Phalcon\Mvc\Controller
{
    public function ajaxError(){

        $this->loadCustomTrans('error', 'error_text');
        $this->flash->error($this->error_text['error_ajax']);
        $this->view->pick(["index/ajaxError"]);
    }
}