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

dispatch forwarding problem

Hi,

I'm trying to do a simple dispatch->forward to display a log in page if a user is not logged in. I tried doing something similar to what is in the documentation and what is done in the INVO application. I have an ArticleController that displays a blog article that extends from ControllerBase. The initialize function in ControllerBase checks if user is logged in and forwards to the LoginController if user hasn't yet logged in.

The issue is that both the LoginController->indexAction and the ArticleController->displayAction() executes. Is that normal behavior with dispatch->forward()? I know I could do a redirect, but wanted to learn how forward is meant to work.

Something like this:

https://mydomain/article/display/5104db3fe73c844d14000000

class ControllerBase extends Phalcon\Mvc\Controller { protected function initialize() { if (!$this->isLoggedIn()) { $this->forward('login/index'); } }

protected function isLoggedIn()
{
    //just a test, will implement session check later
    return false;
}

protected function forward($uri)
{
    $uriParts = explode('/', $uri);

    $this->dispatcher->forward(
        array(
            'controller' => $uriParts[0], 
            'action' => $uriParts[1]
        )
    );
}

}

class ArticleController extends ControllerBase { public function displayAction($article_id) { $this->view->setLayout('threecolumn');

    //Fetch and display article
    try
    {
        $articles = Article::findById($id);
        $this->view->setVar('title', $articles->title );
        $this->view->setVar('description', $articles->description );
        $this->view->setVar('date', $articles->date );
        $this->view->setVar('author', $articles->author);
        $this->view->setVar('text', $articles->text);
    }
    catch(Exception $ex)
    {
        //Sorry Not Found Message
        $msg = $ex->getMessage();
        $this->view->setVar('errors', $msg);
    }
}
}

}



98.9k

The 'initialize' method is always executed, it's like the controller constructor

Forward is intended to reduce the number of HTTP requests required to complete the process, it uses the original HTTP request to internally forward the execution to another controller/action in the same module.

Here the docs about forward: https://docs.phalcon.io/en/latest/reference/dispatching.html#forwarding-to-other-actions

What about multi-module applications?

According to the docs about forwarding that was linked to, $dispatcher->forward() accepts a namespace parameter. Could we see an example of this in use?

I'm most interested in how to handle errors in multi-module applications. Can we specify a "catch-all" module/controller/action for an application?

As an example, an application has 2 modules: Frontend and Backend. In the public/index.php bootstrapping file, I want to forward all errors to the Frontend module's ErrorController. I had imagined it would be something like this:

    if ($event->getType() == 'beforeException') {
        switch ($exception->getCode()) {
            case \Phalcon\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
            case \Phalcon\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                $dispatcher->forward(array(
                    'namespace' => 'Project\Frontend\Controllers",
                    'controller' => 'error',
                    'action' => 'show404'
                ));
                return false;
        }
    }

Alternatively, if the error handler could forward to the current module's error controller, that would be acceptable as well.



999
edited Jun '16

What I like to do is to use dispatch:beforeDispatch event to check whether the user is logged in, if not, simply forward the request to login page and return false :) In the index.php bootstrap:

$di->setShared('dispatcher', function(){
    $eventsManager = new EventsManager();
    $eventsManager->attach('dispatch:beforeDispatch', function($event, $dispatcher){
        if(NOT_LOGGED_IN){
            $dispatcher->forward(array(
                'controller' => 'index',
                'action' => 'login',
            ));
            return false;
        }
    });

    $dispatcher = new Dispatcher();
    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
});

Edit: I suggested using dispatch:beforeDispatchLoop, but you can not forward anything from it, as it happens before the dispatching :) So dispatch:beforeDispatch does the trick. Just keep in mind, that ist is triggered before every dispatched action, so if you forward three times, this event is triggered just as many times.