|
| 1 | +# Available Controllers |
| 2 | + |
| 3 | +Controllers in zend-mvc are objects implementing `Zend\Stdlib\DispatchableInterface`. |
| 4 | +That interface describes a single method: |
| 5 | + |
| 6 | +```php |
| 7 | +use Zend\Stdlib\DispatchableInterface; |
| 8 | +use Zend\Stdlib\RequestInterface as Request; |
| 9 | +use Zend\Stdlib\ResponseInterface as Response; |
| 10 | + |
| 11 | +class Foo implements DispatchableInterface |
| 12 | +{ |
| 13 | + public function dispatch(Request $request, Response $response = null) |
| 14 | + { |
| 15 | + // ... do something, and preferably return a Response ... |
| 16 | + } |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +While the pattern is straight-forward, chances are you don't want to implement |
| 21 | +custom dispatch logic for every controller, particularly as it's not unusual or |
| 22 | +uncommon for a single controller to handle several related types of requests. |
| 23 | + |
| 24 | +To provide convenience, zend-mvc also defines several interfaces that, when |
| 25 | +implemented, can provide controllers with additional capabilities. |
| 26 | + |
| 27 | +## Common Interfaces Used With Controllers |
| 28 | + |
| 29 | +### InjectApplicationEvent |
| 30 | + |
| 31 | +The `Zend\Mvc\InjectApplicationEventInterface` hints to the `Application` |
| 32 | +instance that it should inject its `MvcEvent` into the controller itself. Why |
| 33 | +would this be useful? |
| 34 | + |
| 35 | +Recall that the `MvcEvent` composes a number of objects: the `Request` and |
| 36 | +`Response`, naturally, but also the router, the route matches (a `RouteMatch` |
| 37 | +instance), and potentially the "result" of dispatching. |
| 38 | + |
| 39 | +A controller that has the `MvcEvent` injected, then, can retrieve or inject |
| 40 | +these. As an example: |
| 41 | + |
| 42 | +```php |
| 43 | +$matches = $this->getEvent()->getRouteMatch(); |
| 44 | +$id = $matches->getParam('id', false); |
| 45 | +if (! $id) { |
| 46 | + $response = $this->getResponse(); |
| 47 | + $response->setStatusCode(500); |
| 48 | + $this->getEvent()->setResult('Invalid identifier; cannot complete request'); |
| 49 | + return; |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +The `InjectApplicationEventInterface` defines two methods: |
| 54 | + |
| 55 | +```php |
| 56 | +public function setEvent(Zend\EventManager\EventInterface $event); |
| 57 | +public function getEvent(); |
| 58 | +``` |
| 59 | + |
| 60 | +### ServiceLocatorAware |
| 61 | + |
| 62 | +In most cases, you should define your controllers such that dependencies are |
| 63 | +injected by the application's `ServiceManager`, via either constructor arguments |
| 64 | +or setter methods. |
| 65 | + |
| 66 | +However, occasionally you may have objects you wish to use in your controller |
| 67 | +that are only valid for certain code paths. Examples include forms, paginators, |
| 68 | +navigation, etc. In these cases, you may decide that it doesn't make sense to |
| 69 | +inject those objects every time the controller is used. |
| 70 | + |
| 71 | +The `ServiceLocatorAwareInterface` interface hints to the `ServiceManager` that it should inject |
| 72 | +itself into the controller. It defines two simple methods: |
| 73 | + |
| 74 | +```php |
| 75 | +use Zend\ServiceManager\ServiceLocatorInterface; |
| 76 | +use Zend\ServiceManager\ServiceLocatorAwareInterface; |
| 77 | + |
| 78 | +public function setServiceLocator(ServiceLocatorInterface $serviceLocator); |
| 79 | +public function getServiceLocator(); |
| 80 | +``` |
| 81 | + |
| 82 | +> #### ServiceLocatorInterface is deprecated |
| 83 | +> |
| 84 | +> `ServiceLocatorAwareInterface` [was removed from zend-servicemanager v3.0](http://zendframework.github.io/zend-servicemanager/migration/#miscellaneous-interfaces-traits-and-classes), |
| 85 | +> and, as such, starting in zend-mvc 2.7.0, the `AbstractController` |
| 86 | +> implementation no longer implements the interface, though it implements the |
| 87 | +> methods the interface defines; this allows forwards compatibility with |
| 88 | +> zend-servicemanager v3. |
| 89 | +> |
| 90 | +> However, also starting with the zend-mvc 2.7.0 release, `ServiceLocatorAwareInterface` |
| 91 | +> usage is deprecated. We recommend injecting dependencies explicitly instead of |
| 92 | +> pulling them from a composed `ServiceManager` instance. |
| 93 | +> |
| 94 | +> In cases where an object will not be used in all code paths, we recommend |
| 95 | +> splitting into discrete controllers, or using [lazy services](http://zendframework.github.io/zend-servicemanager/lazy-services/). |
| 96 | +
|
| 97 | +### EventManagerAware |
| 98 | + |
| 99 | +Typically, it's nice to be able to tie into a controller's workflow without |
| 100 | +needing to extend it or hardcode behavior into it. The solution for this is to |
| 101 | +use the `EventManager`. |
| 102 | + |
| 103 | +You can hint to the `ServiceManager` that you want an `EventManager` injected by |
| 104 | +implementing the interface `EventManagerAwareInterface`, which tells the |
| 105 | +`ServiceManager` to inject an `EventManager`. |
| 106 | + |
| 107 | +To do this, you define two methods. The first, a setter, should also set any |
| 108 | +`SharedEventManager` identifiers you want to listen on, and the second, a getter, |
| 109 | +should return the composed `EventManager` instance. |
| 110 | + |
| 111 | +```php |
| 112 | +use Zend\EventManager\EventManagerAwareInterface; |
| 113 | +use Zend\EventManager\EventManagerInterface; |
| 114 | + |
| 115 | +public function setEventManager(EventManagerInterface $events); |
| 116 | +public function getEventManager(); |
| 117 | +``` |
| 118 | + |
| 119 | +### Controller Plugins |
| 120 | + |
| 121 | +Code re-use is a common goal for developers. Another common goal is convenience. |
| 122 | +However, this is often difficult to achieve cleanly in abstract, general |
| 123 | +systems. |
| 124 | + |
| 125 | +Within your controllers, you'll often find yourself repeating tasks from one |
| 126 | +controller to another. Some common examples: |
| 127 | + |
| 128 | +- Generating URLs. |
| 129 | +- Redirecting. |
| 130 | +- Setting and retrieving flash messages (self-expiring session messages). |
| 131 | +- Invoking and dispatching additional controllers. |
| 132 | + |
| 133 | +To facilitate these actions while also making them available to alternate |
| 134 | +controller implementations, we've created a `PluginManager` implementation for |
| 135 | +the controller layer, `Zend\Mvc\Controller\PluginManager`, building on the |
| 136 | +`Zend\ServiceManager\AbstractPluginManager` functionality. To utilize it, |
| 137 | +implement the `setPluginManager(PluginManager $plugins)` method, and set up your |
| 138 | +code to use the controller-specific implementation by default: |
| 139 | + |
| 140 | +```php |
| 141 | +use Zend\Mvc\Controller\PluginManager; |
| 142 | + |
| 143 | +public function setPluginManager(PluginManager $plugins) |
| 144 | +{ |
| 145 | + $this->plugins = $plugins; |
| 146 | + $this->plugins->setController($this); |
| 147 | + |
| 148 | + return $this; |
| 149 | +} |
| 150 | + |
| 151 | +public function getPluginManager() |
| 152 | +{ |
| 153 | + if (!$this->plugins) { |
| 154 | + $this->setPluginManager(new PluginManager()); |
| 155 | + } |
| 156 | + |
| 157 | + return $this->plugins; |
| 158 | +} |
| 159 | + |
| 160 | +public function plugin($name, array $options = null) |
| 161 | +{ |
| 162 | + return $this->getPluginManager()->get($name, $options); |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +## AbstractActionController |
| 167 | + |
| 168 | +Implementing each of the above interfaces is a lesson in redundancy; you won't |
| 169 | +often want to do it. As such, we've developed abstract, base controllers you |
| 170 | +can extend to get started. |
| 171 | + |
| 172 | +The first is `Zend\Mvc\Controller\AbstractActionController`. This controller |
| 173 | +implements each of the above interfaces, and uses the following assumptions: |
| 174 | + |
| 175 | +- An "action" parameter is expected in the `RouteMatch` object composed in the |
| 176 | + attached `MvcEvent`. If none is found, a `notFoundAction()` is invoked. |
| 177 | +- The "action" parameter is converted to a camelCased format and appended with |
| 178 | + the word "Action" to create a method name. As examples: "foo" maps to |
| 179 | + `fooAction`, "foo-bar" or "foo.bar" or "foo\_bar" to `fooBarAction`. The |
| 180 | + controller then checks to see if that method exists. If not, the |
| 181 | + `notFoundAction()` method is invoked; otherwise, the discovered method is |
| 182 | + called. |
| 183 | +- The results of executing the given action method are injected into the |
| 184 | + `MvcEvent`'s "result" property (via `setResult()`, and accessible via |
| 185 | + `getResult()`). |
| 186 | + |
| 187 | +Essentially, a route mapping to an `AbstractActionController` needs to return |
| 188 | +both the "controller" and "action" keys in its matches. |
| 189 | + |
| 190 | +Creation of action controllers looks like the following example: |
| 191 | + |
| 192 | +```php |
| 193 | +namespace Foo\Controller; |
| 194 | + |
| 195 | +use Zend\Mvc\Controller\AbstractActionController; |
| 196 | + |
| 197 | +class BarController extends AbstractActionController |
| 198 | +{ |
| 199 | + public function bazAction() |
| 200 | + { |
| 201 | + return ['title' => __METHOD__]; |
| 202 | + } |
| 203 | + |
| 204 | + public function batAction() |
| 205 | + { |
| 206 | + return ['title' => __METHOD__]; |
| 207 | + } |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +### Interfaces and Collaborators |
| 212 | + |
| 213 | +`AbstractActionController` implements each of the following interfaces: |
| 214 | + |
| 215 | +- `Zend\Stdlib\DispatchableInterface` |
| 216 | +- `Zend\Mvc\InjectApplicationEventInterface` |
| 217 | +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc |
| 218 | + 2.7.0, only the methods defined by the interface, not the interface itself) |
| 219 | +- `Zend\EventManager\EventManagerAwareInterface` |
| 220 | + |
| 221 | +The composed `EventManager` will be configured to listen on the following contexts: |
| 222 | + |
| 223 | +- `Zend\Stdlib\DispatchableInterface` |
| 224 | +- `Zend\Mvc\Controller\AbstractActionController` |
| 225 | +- `Zend\Mvc\Controller\AbstractController` |
| 226 | + |
| 227 | +Additionally, if you extend the class, it will listen on the name of the |
| 228 | +extending class. |
| 229 | + |
| 230 | +## AbstractRestfulController |
| 231 | + |
| 232 | +`Zend\Mvc\Controller\AbstractRestfulController` provides a native RESTful |
| 233 | +implementation that maps HTTP request methods to controller methods, using the |
| 234 | +following matrix: |
| 235 | + |
| 236 | +- **GET** maps to either `get()` or `getList()`, depending on whether or not an |
| 237 | + "id" parameter is found in the route matches. If one is, it is passed as an |
| 238 | + argument to `get()`; if not, `getList()` is invoked. In the former case, you |
| 239 | + should provide a representation of the given entity with that identification; |
| 240 | + in the latter, you should provide a list of entities. |
| 241 | +- **POST** maps to `create()`. That method expects a `$data` argument, usually |
| 242 | + the `$_POST` superglobal array. The data should be used to create a new |
| 243 | + entity, and the response should typically be an HTTP 201 response with the |
| 244 | + Location header indicating the URI of the newly created entity and the |
| 245 | + response body providing the representation. |
| 246 | +- **PUT** maps to `update()`, and requires that an "id" parameter exists in the |
| 247 | + route matches; that value is passed as an argument to the method. It should |
| 248 | + attempt to update the given entity, and, if successful, return either a 200 or |
| 249 | + 202 response status, as well as the representation of the entity. |
| 250 | +- **DELETE** maps to `delete()`, and requires that an "id" parameter exists in |
| 251 | + the route matches; that value is passed as an argument to the method. It |
| 252 | + should attempt to delete the given entity, and, if successful, return either a |
| 253 | + 200 or 204 response status. |
| 254 | + |
| 255 | +Additionally, you can map "action" methods to the `AbstractRestfulController`, |
| 256 | +just as you would in the `AbstractActionController`; these methods will be |
| 257 | +suffixed with "Action", differentiating them from the RESTful methods listed |
| 258 | +above. This allows you to perform such actions as providing forms used to submit |
| 259 | +to the various RESTful methods, or to add RPC methods to your RESTful API. |
| 260 | + |
| 261 | +### Interfaces and Collaborators |
| 262 | + |
| 263 | +`AbstractRestfulController` implements each of the following interfaces: |
| 264 | + |
| 265 | +- `Zend\Stdlib\DispatchableInterface` |
| 266 | +- `Zend\Mvc\InjectApplicationEventInterface` |
| 267 | +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc |
| 268 | + 2.7.0, only the methods defined by the interface, not the interface itself) |
| 269 | +- `Zend\EventManager\EventManagerAwareInterface` |
| 270 | + |
| 271 | +The composed `EventManager` will be configured to listen on the following contexts: |
| 272 | + |
| 273 | +- `Zend\Stdlib\DispatchableInterface` |
| 274 | +- `Zend\Mvc\Controller\AbstractRestfulController` |
| 275 | +- `Zend\Mvc\Controller\AbstractController` |
| 276 | + |
| 277 | +Additionally, if you extend the class, it will listen on the name of the |
| 278 | +extending class. |
| 279 | + |
| 280 | +## AbstractConsoleController |
| 281 | + |
| 282 | +`Zend\Mvc\Controller\AbstractConsoleController` extends from [AbstractActionController](#abstractactioncontroller) |
| 283 | +and provides the following functionality: |
| 284 | + |
| 285 | +- The method `setConsole(Zend\Console\Adapter\AdapterInterface $console)` allows |
| 286 | + injecting a console adapter representing the current console environment. By |
| 287 | + default, the `ControllerManager` will inject this for you as part of |
| 288 | + controller instantiation. |
| 289 | +- The method `getConsole()` allows you to retrieve the current console adapter |
| 290 | + instance, allowing you to retrieve console capabilities and generate console |
| 291 | + output. |
| 292 | +- The `dispatch()` method will throw an exception if invoked in a non-console |
| 293 | + environment, ensuring that you do not need to do any checks within your action |
| 294 | + methods for the environment. |
| 295 | + |
| 296 | +### Interfaces and Collaborators |
| 297 | + |
| 298 | +`AbstractRestfulController` implements each of the following interfaces: |
| 299 | + |
| 300 | +- `Zend\Stdlib\DispatchableInterface` |
| 301 | +- `Zend\Mvc\InjectApplicationEventInterface` |
| 302 | +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc |
| 303 | + 2.7.0, only the methods defined by the interface, not the interface itself) |
| 304 | +- `Zend\EventManager\EventManagerAwareInterface` |
| 305 | + |
| 306 | +The composed `EventManager` will be configured to listen on the following contexts: |
| 307 | + |
| 308 | +- `Zend\Stdlib\DispatchableInterface` |
| 309 | +- `Zend\Mvc\Controller\AbstractConsoleController` |
| 310 | +- `Zend\Mvc\Controller\AbstractActionController` |
| 311 | +- `Zend\Mvc\Controller\AbstractController` |
| 312 | + |
| 313 | +Additionally, if you extend the class, it will listen on the name of the |
| 314 | +extending class. |
0 commit comments