Posts Tagged ZF2

End a Request with a Zend Framework 2 Controller Plugin

By default, Zend Framework 2 (ZF2) controller plugins cannot force the request to end. Typically, the controller action calls plugin methods and then does what it wishes with the returned value. However, there are some circumstances (such as user authentication), where you may want to end the request if certain conditions are met in the controller plugin.

For example:
Say you want to return the currently authenticated user using a controller plugin, and if there is no authenticated user, return a 401 status code. When your controller calls the plugin, you would have to check the return value every time and have boilerplate code to set the status code in the response and return the response from your controller action.

There is a better way:
Instead of having to check in your controller, wouldn’t it be better if you could just have the Plugin do the work: set the status code and return the response to the client?

This class makes that possible. By emitting MvcEvent:EVENT_FINISH, it triggers the listeners to send the response to the client. By immediately exiting the application and ending the request, we ensure that MvcEvent:EVENT_FINISH is emitted only once.

/**
 * Plugins interiting from this superclass will be able to terminate the request by calling sendResponse()
 *
 * Class RequestTerminationPlugin
 * @package Application\Controller\Plugin
 */
abstract class SendResponsePlugin extends AbstractPlugin
{
 
    /**
     * @var MvcEvent
     */
    private $event;
 
 
    /**
     * Sends the response and terminates.
     * @param ResponseInterface $response
     */
    protected function sendResponse(ResponseInterface $response)
    {
        $e = $this->getEvent();
        $e->setResponse($response);
 
        $e->getApplication()->getEventManager()->trigger(MvcEvent::EVENT_FINISH, $e);
        exit();
    }
 
 
    /**
     * Get the event
     *
     * @return MvcEvent
     * @throws DomainException if unable to find event
     */
    protected function getEvent()
    {
        if ($this->event) {
            return $this->event;
        }
 
        $controller = $this->getController();
        if (!$controller instanceof InjectApplicationEventInterface) {
            throw new DomainException(get_class($this) . ' requires a controller that implements InjectApplicationEventInterface');
        }
 
        $event = $controller->getEvent();
        if (!$event instanceof MvcEvent) {
            $params = $event->getParams();
            $event  = new MvcEvent();
            $event->setParams($params);
        }
        $this->event = $event;
 
        return $this->event;
    }
 
}

, , , ,

No Comments

Getting started with AbstractRestfulController

Zend Framework 2 introduced a new controller abstract class: AbstractRestfulController (more docs here). This is used to create restful services (which will communicate with javascript in the browser, for example).

Controllers implementing AbstractRestfulController, can expose normal actions in addition to the REST methods. Following the Zend convention, these the name of these methods must end in “Action”.

Example Rest Controller, with only the getList() method populated.

class TestRestController extends AbstractRestfulController
{
    public function indexAction()
    {
        return new ViewModel();
    }
 
    /**
     * Return list of resources
     *
     * @return mixed
     */
    public function getList()
    {
        return new JsonModel(array(
            array('name' => 'test'),
            array('name' => 'second')
        ));
    }
 
    /**
     * Return single resource
     *
     * @param  mixed $id
     * @return mixed
     */
    public function get($id)
    {
        //TODO: Implement Method
    }
 
    /**
     * Create a new resource
     *
     * @param  mixed $data
     * @return mixed
     */
    public function create($data)
    {
        //TODO: Implement Method
    }
 
    /**
     * Update an existing resource
     *
     * @param  mixed $id
     * @param  mixed $data
     * @return mixed
     */
    public function update($id, $data)
    {
        //TODO: Implement Method
    }
 
    /**
     * Delete an existing resource
     *
     * @param  mixed $id
     * @return mixed
     */
    public function delete($id)
    {
        //TODO: Implement Method
    }
}

I found the dispatch logic for AbstractRestfulController a little confusing.
If there is an available “action” value on the request object, the controller attempts to find the corresponding action method. If there is no action value, then it dispatches to the REST methods. This essentially means that we will have to setup custom routes to dispatch to AbstractRestfulControllers as the “action” value will typically have a default value of “index” if we are following the traditional route configuration.

If you are getting 404 Not Found errors, when you are expecting the REST methods to be executing, this is probably the problem.

Example route configuration (In module.config.php):

return array(
    'controllers' => array(
        'invokables' => array(
            'Application\Controller\TestRest' => 'Application\Controller\TestRestController',
        ),
    ),
 
    'router' => array(
        'routes' => array(
            'rest' => array(
                'type'    => 'Literal',
                'options' => array(
                    'route'    => '/rest',
                    'defaults' => array(
                        '__NAMESPACE__' => 'Application\Controller',
                    ),
                ),
                'may_terminate' => true,
                'child_routes' => array(
                    'default' => array(
                        'type'    => 'Segment',
                        'options' => array(
                            'route'    => '/:controller[/:id][/]',
                            'constraints' => array(
                                'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
                            ),
                            'defaults' => array(
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ),

To get the controller to just render the JSON in the JsonModel returned by the getList method, we have to add a view manager strategy to the config:

return array(
    'view_manager' => array(
        'template_path_stack' => array(
            __DIR__ . '/../view',
        ),
        'strategies' => array(
            'ViewJsonStrategy',
        ),
    ),
);

, ,

4 Comments