Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit eed620c

Browse files
committed
Merge branch 'hotfix/56'
Close #56
2 parents 7d840a5 + 52f83c2 commit eed620c

10 files changed

+3205
-0
lines changed

doc/book/zend.mvc.controllers.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Available Controllers
2+
3+
Controllers in the MVC layer simply need to be objects implementing
4+
`Zend\Stdlib\DispatchableInterface`. 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 this pattern is simple enough, chances are you don't want to implement custom dispatch logic
21+
for every controller (particularly as it's not unusual or uncommon for a single controller to handle
22+
several related types of requests).
23+
24+
The MVC also defines several interfaces that, when implemented, can provide controllers with
25+
additional capabilities.
26+
27+
## Common Interfaces Used With Controllers
28+
29+
### InjectApplicationEvent
30+
31+
The `Zend\Mvc\InjectApplicationEventInterface` hints to the `Application` instance that it should
32+
inject its `MvcEvent` into the controller itself. Why would this be useful?
33+
34+
Recall that the `MvcEvent` composes a number of objects: the `Request` and `Response`, naturally,
35+
but also the router, the route matches (a `RouteMatch` instance), and potentially the "result" of
36+
dispatching.
37+
38+
A controller that has the `MvcEvent` injected, then, can retrieve or inject these. As an example:
39+
40+
```php
41+
$matches = $this->getEvent()->getRouteMatch();
42+
$id = $matches->getParam('id', false);
43+
if (!$id) {
44+
$response = $this->getResponse();
45+
$response->setStatusCode(500);
46+
$this->getEvent()->setResult('Invalid identifier; cannot complete request');
47+
return;
48+
}
49+
```
50+
51+
The `InjectApplicationEventInterface` defines simply two methods:
52+
53+
```php
54+
public function setEvent(Zend\EventManager\EventInterface $event);
55+
public function getEvent();
56+
```
57+
58+
### ServiceLocatorAware
59+
60+
In most cases, you should define your controllers such that dependencies are injected by the
61+
application's `ServiceManager`, via either constructor arguments or setter methods.
62+
63+
However, occasionally you may have objects you wish to use in your controller that are only valid
64+
for certain code paths. Examples include forms, paginators, navigation, etc. In these cases, you may
65+
decide that it doesn't make sense to inject those objects every time the controller is used.
66+
67+
The `ServiceLocatorAwareInterface` interface hints to the `ServiceManager` that it should inject
68+
itself into the controller. It defines two simple methods:
69+
70+
```php
71+
use Zend\ServiceManager\ServiceLocatorInterface;
72+
use Zend\ServiceManager\ServiceLocatorAwareInterface;
73+
74+
public function setServiceLocator(ServiceLocatorInterface $serviceLocator);
75+
public function getServiceLocator();
76+
```
77+
78+
### EventManagerAware
79+
80+
Typically, it's nice to be able to tie into a controller's workflow without needing to extend it or
81+
hardcode behavior into it. The solution for this at the framework level is to use the
82+
`EventManager`.
83+
84+
You can hint to the `ServiceManager` that you want an `EventManager` injected by implementing the
85+
interface `EventManagerAwareInterface`, which tells the `ServiceManager` to inject an
86+
`EventManager`.
87+
88+
You define two methods. The first, a setter, should also set any `EventManager` identifiers you want
89+
to listen on, and the second, a getter, should simply return the composed `EventManager` instance.
90+
91+
```php
92+
use Zend\EventManager\EventManagerAwareInterface;
93+
use Zend\EventManager\EventManagerInterface;
94+
95+
public function setEventManager(EventManagerInterface $events);
96+
public function getEventManager();
97+
```
98+
99+
### Controller Plugins
100+
101+
Code re-use is a common goal for developers. Another common goal is convenience. However, this is
102+
often difficult to achieve cleanly in abstract, general systems.
103+
104+
Within your controllers, you'll often find yourself repeating tasks from one controller to another.
105+
Some common examples:
106+
107+
- Generating URLs
108+
- Redirecting
109+
- Setting and retrieving flash messages (self-expiring session messages)
110+
- Invoking and dispatching additional controllers
111+
112+
To facilitate these actions while also making them available to alternate controller
113+
implementations, we've created a `PluginManager` implementation for the controller layer,
114+
`Zend\Mvc\Controller\PluginManager`, building on the `Zend\ServiceManager\AbstractPluginManager`
115+
functionality. To utilize it, you simply need to implement the `setPluginManager(PluginManager
116+
$plugins)` method, and set up your code to use the controller-specific implementation by default:
117+
118+
```php
119+
use Zend\Mvc\Controller\PluginManager;
120+
121+
public function setPluginManager(PluginManager $plugins)
122+
{
123+
$this->plugins = $plugins;
124+
$this->plugins->setController($this);
125+
126+
return $this;
127+
}
128+
129+
public function getPluginManager()
130+
{
131+
if (!$this->plugins) {
132+
$this->setPluginManager(new PluginManager());
133+
}
134+
135+
return $this->plugins;
136+
}
137+
138+
public function plugin($name, array $options = null)
139+
{
140+
return $this->getPluginManager()->get($name, $options);
141+
}
142+
```
143+
144+
## The AbstractActionController
145+
146+
Implementing each of the above interfaces is a lesson in redundancy; you won't often want to do it.
147+
As such, we've developed two abstract, base controllers you can extend to get started.
148+
149+
The first is `Zend\Mvc\Controller\AbstractActionController`. This controller implements each of the
150+
above interfaces, and uses the following assumptions:
151+
152+
- An "action" parameter is expected in the `RouteMatch` object composed in the attached `MvcEvent`.
153+
If none is found, a `notFoundAction()` is invoked.
154+
- The "action" parameter is converted to a camelCased format and appended with the word "Action" to
155+
create a method name. As examples: "foo" maps to "fooAction", "foo-bar" or "foo.bar" or "foo\_bar"
156+
to "fooBarAction". The controller then checks to see if that method exists. If not, the
157+
`notFoundAction()` method is invoked; otherwise, the discovered method is called.
158+
- The results of executing the given action method are injected into the `MvcEvent`'s "result"
159+
property (via `setResult()`, and accessible via `getResult()`).
160+
161+
Essentially, a route mapping to an `AbstractActionController` needs to return both "controller" and
162+
"action" keys in its matches.
163+
164+
Creation of action controllers is then reasonably trivial:
165+
166+
```php
167+
namespace Foo\Controller;
168+
169+
use Zend\Mvc\Controller\AbstractActionController;
170+
171+
class BarController extends AbstractActionController
172+
{
173+
public function bazAction()
174+
{
175+
return array('title' => __METHOD__);
176+
}
177+
178+
public function batAction()
179+
{
180+
return array('title' => __METHOD__);
181+
}
182+
}
183+
```
184+
185+
### Interfaces and Collaborators
186+
187+
`AbstractActionController` implements each of the following interfaces:
188+
189+
- `Zend\Stdlib\DispatchableInterface`
190+
- `Zend\Mvc\InjectApplicationEventInterface`
191+
- `Zend\ServiceManager\ServiceLocatorAwareInterface`
192+
- `Zend\EventManager\EventManagerAwareInterface`
193+
194+
The composed `EventManager` will be configured to listen on the following contexts:
195+
196+
- `Zend\Stdlib\DispatchableInterface`
197+
- `Zend\Mvc\Controller\AbstractActionController`
198+
- `Zend\Mvc\Controller\AbstractController`
199+
200+
Additionally, if you extend the class, it will listen on the extending class's name.
201+
202+
## The AbstractRestfulController
203+
204+
The second abstract controller ZF2 provides is `Zend\Mvc\Controller\AbstractRestfulController`. This
205+
controller provides a native RESTful implementation that simply maps HTTP request methods to
206+
controller methods, using the following matrix:
207+
208+
- **GET** maps to either `get()` or `getList()`, depending on whether or not an "id" parameter is
209+
found in the route matches. If one is, it is passed as an argument to `get()`; if not, `getList()`
210+
is invoked. In the former case, you should provide a representation of the given entity with that
211+
identification; in the latter, you should provide a list of entities.
212+
- **POST** maps to `create()`. That method expects a `$data` argument, usually the `$_POST`
213+
superglobal array. The data should be used to create a new entity, and the response should typically
214+
be an HTTP 201 response with the Location header indicating the URI of the newly created entity and
215+
the response body providing the representation.
216+
- **PUT** maps to `update()`, and requires that an "id" parameter exists in the route matches; that
217+
value is passed as an argument to the method. It should attempt to update the given entity, and, if
218+
successful, return either a 200 or 202 response status, as well as the representation of the entity.
219+
- **DELETE** maps to `delete()`, and requires that an "id" parameter exists in the route matches;
220+
that value is passed as an argument to the method. It should attempt to delete the given entity,
221+
and, if successful, return either a 200 or 204 response status.
222+
223+
Additionally, you can map "action" methods to the `AbstractRestfulController`, just as you would in
224+
the `AbstractActionController`; these methods will be suffixed with "Action", differentiating them
225+
from the RESTful methods listed above. This allows you to perform such actions as providing forms
226+
used to submit to the various RESTful methods, or to add RPC methods to your RESTful API.
227+
228+
### Interfaces and Collaborators
229+
230+
`AbstractRestfulController` implements each of the following interfaces:
231+
232+
- `Zend\Stdlib\DispatchableInterface`
233+
- `Zend\Mvc\InjectApplicationEventInterface`
234+
- `Zend\ServiceManager\ServiceLocatorAwareInterface`
235+
- `Zend\EventManager\EventManagerAwareInterface`
236+
237+
The composed `EventManager` will be configured to listen on the following contexts:
238+
239+
- `Zend\Stdlib\DispatchableInterface`
240+
- `Zend\Mvc\Controller\AbstractRestfulController`
241+
- `Zend\Mvc\Controller\AbstractController`
242+
243+
Additionally, if you extend the class, it will listen on the extending class's name.

doc/book/zend.mvc.examples.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Examples
2+
3+
## Controllers
4+
5+
### Accessing the Request and Response
6+
7+
When using `AbstractActionController` or `AbstractRestfulController`, the request and response
8+
object are composed directly into the controller as soon as `dispatch()` is called. You may access
9+
them in the following ways:
10+
11+
```php
12+
// Using explicit accessor methods
13+
$request = $this->getRequest();
14+
$response = $this->getResponse();
15+
16+
// Using direct property access
17+
$request = $this->request;
18+
$response = $this->response;
19+
```
20+
21+
Additionally, if your controller implements `InjectApplicationEventInterface` (as both
22+
`AbstractActionController` and `AbstractRestfulController` do), you can access these objects from
23+
the attached `MvcEvent`:
24+
25+
```php
26+
$event = $this->getEvent();
27+
$request = $event->getRequest();
28+
$response = $event->getResponse();
29+
```
30+
31+
The above can be useful when composing event listeners into your controller.
32+
33+
### Accessing routing parameters
34+
35+
The parameters returned when routing completes are wrapped in a `Zend\Mvc\Router\RouteMatch` object.
36+
This object is detailed in the section on \[Routing\](zend.mvc.routing).
37+
38+
Within your controller, if you implement `InjectApplicationEventInterface` (as both
39+
`AbstractActionController` and `AbstractRestfulController` do), you can access this object from the
40+
attached `MvcEvent`:
41+
42+
```php
43+
$event = $this->getEvent();
44+
$matches = $event->getRouteMatch();
45+
```
46+
47+
Once you have the `RouteMatch` object, you can pull parameters from it.
48+
49+
The same can be done using the Params plugin<zend.mvc.controller-plugins.params>.
50+
51+
### Returning early
52+
53+
You can effectively short-circuit execution of the application at any point by returning a
54+
`Response` from your controller or any event. When such a value is discovered, it halts further
55+
execution of the event manager, bubbling up to the `Application` instance, where it is immediately
56+
returned.
57+
58+
As an example, the `Redirect` plugin returns a `Response`, which can be returned immediately so as
59+
to complete the request as quickly as possible. Other use cases might be for returning JSON or XML
60+
results from web service endpoints, returning "401 Unauthorized" results, etc.
61+
62+
## Bootstrapping
63+
64+
### Registering module-specific listeners
65+
66+
Often you may want module-specific listeners. As an example, this would be a simple and effective
67+
way to introduce authorization, logging, or caching into your application.
68+
69+
Each `Module` class can have an optional `onBootstrap()` method. Typically, you'll do
70+
module-specific configuration here, or setup event listeners for you module here. The
71+
`onBootstrap()` method is called for **every** module on **every** page request and should **only**
72+
be used for performing **lightweight** tasks such as registering event listeners.
73+
74+
The base `Application` class shipped with the framework has an `EventManager` associated with it,
75+
and once the modules are initialized, it triggers the \[bootstrap\](zend.mvc.mvc-event.bootstrap)
76+
event, with a `getApplication()` method on the event.
77+
78+
So, one way to accomplish module-specific listeners is to listen to that event, and register
79+
listeners at that time. As an example:
80+
81+
```php
82+
namespace SomeCustomModule;
83+
84+
class Module
85+
{
86+
/**
87+
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
88+
* @return void
89+
*/
90+
public function onBootstrap($e)
91+
{
92+
$application = $e->getApplication();
93+
$config = $application->getConfig();
94+
$view = $application->getServiceManager()->get('ViewHelperManager');
95+
// You must have these keys in you application config
96+
$view->headTitle($config['view']['base_title']);
97+
98+
// This is your custom listener
99+
$listener = new Listeners\ViewListener();
100+
$listener->setView($view);
101+
$application->getEventManager()->attachAggregate($listener);
102+
}
103+
}
104+
```
105+
106+
The above demonstrates several things. First, it demonstrates a listener on the application's
107+
\[bootstrap\](zend.mvc.mvc-event.bootstrap) event (the `onBootstrap()` method). Second, it
108+
demonstrates that listener, and how it can be used to register listeners with the application. It
109+
grabs the `Application` instance; from the `Application`, it is able to grab the attached service
110+
manager and configuration. These are then used to retrieve the view, configure some helpers, and
111+
then register a listener aggregate with the application event manager.

0 commit comments

Comments
 (0)