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

Throw exception before any action calls in controller and handle it in ErrorController

Hi All,

I have a userController that handle all CRUD of user (add/delete/create/update). I want to check user existance in top of all actions before any action calls and if user does not exist (with passed id) then throw exception, then catch it in my ErrorController.

If I do this check in every action, everything works. but I cannot found ant way to handle this in initialize or any point of dispaching loop steps.

This is my DispatchErrorPlugin:

class DispatchErrorPlugin extends PhPlugin
{
    public function beforeException(PhEvent $event, PhDispatcher $dispatcher, $exception)
    {
        //Handle 404 exceptions
        if ($exception instanceof PhDispatchException) {
            $dispatcher->setParam('exception', $exception);
            $dispatcher->forward(array(
                'controller' => 'error',
                'action' => 'show404'
            ));
            return false;
        }

        //Handle other exceptions
        $dispatcher->setParam('exception', $exception);
        $dispatcher->forward(array(
            'controller' => 'error',
            'action' => 'show500'
        ));

        return false;
    }
} 

and in bootstrap I have attached this to dispatcher :

$errorHandller     = new DispatchErrorPlugin();
$evManager->attach('dispatch:beforeException', $errorHandller);

I want to do somthing like this in controller:

use Phalcon\Mvc\Dispatcher\Exception as PhDispatchException;

class ManageCourseController extends Phalcon\Mvc\Controller
{
    public function initialize()
    {
        $userid = $this->dispatcher->getParam('id', 'int');
        $user = Users::findFirstById($userid);

        if(!$user) {
            throw new PhDispatchException('User not found!');
        }
    }

    public function showAction()
    {
    }

    public function deleteAction()
    {
    }
}

This exception catched in bootstrap $application->handle()->getContent() try/catch block!

Any adea?

Thanks.

edited Jun '15

While browsing the source code, It seams that the userException just handles when actionMethod calls!

phalcon/dispatcher.zep

            try {

                // We update the latest value produced by the latest handler
                let this->_returnedValue = call_user_func_array([handler, actionMethod], params), // <- HERE!
                    this->_lastHandler = handler;

            } catch \Exception, e {
                if this->{"_handleException"}(e) === false {
                    if this->_finished === false {
                        continue;
                    }
                } else {
                    throw e;
                }
            }

and only this section of code, fires dispatch:beforeException :

phalcon/mvc/dispatcher.zep

    /**
     * Handles a user exception
     */
    protected function _handleException(<\Exception> exception)
    {
        var eventsManager;
        let eventsManager = <ManagerInterface> this->_eventsManager;
        if typeof eventsManager == "object" {
            if eventsManager->fire("dispatch:beforeException", this, exception) === false {
                return false;
            }
        }
    }

So any workaround to handle userException and fire dispatch:beforeException before any action call?

Thanks.

It solve my problem, but it smells not DRY and good design!

In my controller :

    /**
     * Initializes the controller
     */
    public function initialize()
    {
        $userid = $this->dispatcher->getParam('id', 'int');
        $user = Users::findFirstById($userid);

        $this->dispatcher->getEventsManager()->fire(
            "dispatch:beforeException",
            $this->dispatcher, 
            new PhDispatchException('User not found!')
        );
    }

any other suggestion axists?

What about?

use Phalcon\Mvc\Dispatcher\Exception as PhDispatchException;

class ManageCourseController extends Phalcon\Mvc\Controller
{
    private $user;

    public function beforeExecuteRoute()
    {
        $userid = $this->dispatcher->getParam('id', 'int');
        $this->user = Users::findFirstById($userid);
        if (!$this->user) {
            $this->dispatcher->forward(['controller' => 'errors', 'action' => 'show500']);
            return false;
        }
    }

    public function showAction()
    {
    }

    public function deleteAction()
    {
    }
}

This works as workaround but in this case I should attach message to view or setParam to dispatcher then show message in 404 page. Exactly what my DispatchErrorPlugin does!

This will depart the central Dispatch Error handling in my POV.

I think it would be beter that we could use user defined exception in all steps of DispatchLoop and it fire beforeException event.

Thanks.

What about?

use Phalcon\Mvc\Dispatcher\Exception as PhDispatchException;

class ManageCourseController extends Phalcon\Mvc\Controller
{
  private $user;

   public function beforeExecuteRoute()
   {
       $userid = $this->dispatcher->getParam('id', 'int');
       $this->user = Users::findFirstById($userid);
       if (!$this->user) {
           $this->dispatcher->forward(['controller' => 'errors', 'action' => 'show500']);
          return false;
       }
   }

   public function showAction()
   {
   }

   public function deleteAction()
   {
   }
}


14.8k
Accepted
answer

It seems that this behavior is predetermined, and there is no way to do such things (at least in current Phalcon versions)

In DOC:

Only exceptions produced by the dispatcher and exceptions produced in the executed action are notified in the ‘beforeException’ events. Exceptions produced in listeners or controller events are redirected to the latest try/catch.



1.3k
edited Jan '17

I solved the issue this way:

class DispatchErrorPlugin extends PhPlugin
{
    public function beforeException(PhEvent $event, PhDispatcher $dispatcher, $exception)
    {
        // ...
        if ($exception instanceof DispatchException) {
            // ...
            return true;
        }
        // ...
    }
}
use Phalcon\Mvc\Dispatcher\Exception as PhDispatchException;

class ManageCourseController extends Phalcon\Mvc\Controller
{
    public function initialize()
    {
        try {
            $userid = $this->dispatcher->getParam('id', 'int');
            $user = Users::findFirstById($userid);

            if(!$user) {
                throw new \Exception('User not found!');
            }
        } catch (\Exception $e) {
            throw PhDispatchException($e->getMessage());
        }
    }

    // ...
}