Controllers in the MVC layer simply need to be objects implementing Zend\Stdlib\DispatchableInterface. That interface describes a single method:
1 2 3 4 5 6 7 8 9 10 11 | use Zend\Stdlib\DispatchableInterface;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Stdlib\ResponseInterface as Response;
class Foo implements DispatchableInterface
{
public function dispatch(Request $request, Response $response = null)
{
// ... do something, and preferably return a Response ...
}
}
|
While this pattern is simple enough, chances are you don’t want to implement custom dispatch logic for every controller (particularly as it’s not unusual or uncommon for a single controller to handle several related types of requests).
The MVC also defines several interfaces that, when implemented, can provide controllers with additional capabilities.
The Zend\Mvc\InjectApplicationEventInterface hints to the Application instance that it should inject its MvcEvent into the controller itself. Why would this be useful?
Recall that the MvcEvent composes a number of objects: the Request and Response, naturally, but also the router, the route matches (a RouteMatch instance), and potentially the “result” of dispatching.
A controller that has the MvcEvent injected, then, can retrieve or inject these. As an example:
1 2 3 4 5 6 7 8 | $matches = $this->getEvent()->getRouteMatch();
$id = $matches->getParam('id', false);
if (!$id) {
$response = $this->getResponse();
$response->setStatusCode(500);
$this->getEvent()->setResult('Invalid identifier; cannot complete request');
return;
}
|
The InjectApplicationEventInterface defines simply two methods:
1 2 | public function setEvent(Zend\EventManager\EventInterface $event);
public function getEvent();
|
In most cases, you should define your controllers such that dependencies are injected by the application’s ServiceManager, via either constructor arguments or setter methods.
However, occasionally you may have objects you wish to use in your controller that are only valid for certain code paths. Examples include forms, paginators, navigation, etc. In these cases, you may decide that it doesn’t make sense to inject those objects every time the controller is used.
The ServiceLocatorAwareInterface interface hints to the ServiceManager that it should inject itself into the controller. It defines two simple methods:
1 2 3 4 5 | use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator);
public function getServiceLocator();
|
Typically, it’s nice to be able to tie into a controller’s workflow without needing to extend it or hardcode behavior into it. The solution for this at the framework level is to use the EventManager.
You can hint to the ServiceManager that you want an EventManager injected by implementing the interface EventManagerAwareInterface, which tells the ServiceManager to inject an EventManager.
You define two methods. The first, a setter, should also set any EventManager identifiers you want to listen on, and the second, a getter, should simply return the composed EventManager instance.
1 2 3 4 5 | use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
public function setEventManager(EventManagerInterface $events);
public function getEventManager();
|
Code re-use is a common goal for developers. Another common goal is convenience. However, this is often difficult to achieve cleanly in abstract, general systems.
Within your controllers, you’ll often find yourself repeating tasks from one controller to another. Some common examples:
To facilitate these actions while also making them available to alternate controller implementations, we’ve created a PluginManager implementation for the controller layer, Zend\Mvc\Controller\PluginManager, building on the Zend\ServiceManager\AbstractPluginManager functionality. To utilize it, you simply need to implement the setPluginManager(PluginManager $plugins) method, and set up your code to use the controller-specific implementation by default:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | use Zend\Mvc\Controller\PluginManager;
public function setPluginManager(PluginManager $plugins)
{
$this->plugins = $plugins;
$this->plugins->setController($this);
return $this;
}
public function getPluginManager()
{
if (!$this->plugins) {
$this->setPluginManager(new PluginManager());
}
return $this->plugins;
}
public function plugin($name, array $options = null)
{
return $this->getPluginManager()->get($name, $options);
}
|
Implementing each of the above interfaces is a lesson in redundancy; you won’t often want to do it. As such, we’ve developed two abstract, base controllers you can extend to get started.
The first is Zend\Mvc\Controller\AbstractActionController. This controller implements each of the above interfaces, and uses the following assumptions:
Essentially, a route mapping to an AbstractActionController needs to return both “controller” and “action” keys in its matches.
Creation of action controllers is then reasonably trivial:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | namespace Foo\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class BarController extends AbstractActionController
{
public function bazAction()
{
return array('title' => __METHOD__);
}
public function batAction()
{
return array('title' => __METHOD__);
}
}
|
AbstractActionController implements each of the following interfaces:
The composed EventManager will be configured to listen on the following contexts:
Additionally, if you extend the class, it will listen on the extending class’s name.
The second abstract controller ZF2 provides is Zend\Mvc\Controller\AbstractRestfulController. This controller provides a native RESTful implementation that simply maps HTTP request methods to controller methods, using the following matrix:
Additionally, you can map “action” methods to the AbstractRestfulController, just as you would in the AbstractActionController; these methods will be suffixed with “Action”, differentiating them from the RESTful methods listed above. This allows you to perform such actions as providing forms used to submit to the various RESTful methods, or to add RPC methods to your RESTful API.
AbstractRestfulController implements each of the following interfaces:
The composed EventManager will be configured to listen on the following contexts:
Additionally, if you extend the class, it will listen on the extending class’s name.
The source code of this file is hosted on GitHub. Everyone can update and fix errors in this document with few clicks - no downloads needed.