2

I have a Zend Framework controller with an editAction().

class WidgetController extends BaseController
{
   public function editAction()
   {
       //code here
   }
}

This controller extends a base controller which checks if the user is logged in before allowing the user to edit a record.

class BaseController extends Zend_Controller_Action
{
   public function init()
   {
       if ($this->userNotLoggedIn()) {
           return $this->_redirect('/auth/login');
       }
   }
}

However, now that I am performing an AJAX request, I will be sending a JSON response back, so a redirect will no longer work. I need to stop further controller execution so I can immediately send a response:

class BaseController extends Zend_Controller_Action
{
   public function init()
   {
       if ($this->userNotLoggedIn()) {
           if ($this->_request->isXmlHttpRequest()) {
               $jsonData = Zend_Json::encode(array('error'=>'You are not logged in!'));
               $this->getResponse()
                    ->setHttpResponseCode(401)
                    ->setBody($jsonData)
                    ->setHeader('Content-Type', 'text/json');
               //now stop controller execution so that the WidgetController does not continue
           } else {
               return $this->_redirect('/auth/login');
           }
       }
   }
}

How can I stop controller execution?

Andrew
  • 227,796
  • 193
  • 515
  • 708
  • Although Daff's answer is probably correct, I would more the init logic to a controller plugin so you can easily reuse the logic. – Fatmuemoo Jul 24 '11 at 20:54
  • Actually, I have already done something like that, but that's not really the nature of my question. – Andrew Jul 25 '11 at 04:25
  • A non-elegant solution would be to simply have the script `die();` where you want it to stop. – Eduard Luca Aug 31 '12 at 08:55

2 Answers2

5

I would define the user not being logged in and trying to make an XMLHTTPRequest as an exceptional state and let the error handler deal with it by throwing an exception (which stops dispatching of the current action). That way you are also able to handle other kinds of exceptions that might happen:

class BaseController extends Zend_Controller_Action
{
   public function init()
   {
       if ($this->userNotLoggedIn()) {
           if ($this->_request->isXmlHttpRequest()) {
                throw new Exception('You are not logged in', 401);
           } else {
               return $this->_redirect('/auth/login');
           }
       }
   }
}

class ErrorController extends Zend_Controller_Action
{

    public function errorAction()
    {
        $errors = $this->_getParam('error_handler');
        $exception = $errors->exception;

       if ($this->_request->isXmlHttpRequest()) {
           $jsonData = Zend_Json::encode($exception);
            $jsonData = Zend_Json::encode(array('error'=> $exception->getMessage()));
            $isHttpError = $exception->getCode() > 400 && $exception->getCode();
            $code =  $isHttpError ? $exception->getCode() : 500;
           $this->getResponse()
                ->setHttpResponseCode($code)
                ->setBody($jsonData)
                ->setHeader('Content-Type', 'application/json');
        } else {
            // Render error view
        }
    }
}
debianek
  • 589
  • 1
  • 9
  • 30
Daff
  • 43,734
  • 9
  • 106
  • 120
  • Good idea. I think I'll do that for now. I'd still like to know if there is a way to stop the controller though (like redirect does). – Andrew Jul 22 '11 at 18:46
  • As for that, you can try setting $this->_request->setDispatched(true); but not in init() but in the preDispatch() method. – Daff Jul 22 '11 at 18:52
5

I can think of many ways to stop the controller at this point in your code.

//now stop controller execution so that the WidgetController does not continue

For one, you can replace that line with this the following:

$this->getResponse()->sendResponse();
exit;

That may not be the cleanest but gets the job done rather nicely. The other option is going to be to change the action of the request in the init and let another action handle it. Replace that line with this:

 $this->getRequest()->setActionName('invalid-user');

Because your already inside the dispatcher, it's going to run an action inside your action class whether you want it to or not. Trying to change the request in preDispatch will do nothing to change this dispatch. It's determined at this point to run an action inside your class. So, make an action to handle it.

public function invalidUserAction()
{
    $this->_helper->layout->disableLayout();
    $this->_helper->viewRenderer->setNoRender();
}

For more information see Zend_Controller_Dispatcher_Standard::dispatch.

brady.vitrano
  • 2,256
  • 2
  • 16
  • 26