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

DOCS: Dont mention the inspection of the Accept HTTP Header #84

Merged
merged 4 commits into from
Mar 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 82 additions & 125 deletions doc/book/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,24 +534,26 @@ priority. Typically, you will register this during the bootstrap event.
```php
namespace Content;

use Zend\Mvc\MvcEvent;

class Module
{
/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function onBootstrap($e)
public function onBootstrap(MvcEvent $e)
{
// Register a dispatch event
$app = $e->getParam('application');
$app->getEventManager()->attach('dispatch', [$this, 'setLayout']);
}

/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function setLayout($e)
public function setLayout(MvcEvent $e)
{
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
Expand Down Expand Up @@ -583,16 +585,12 @@ within your application.
- `Zend\View\Strategy\PhpRendererStrategy`. This strategy is a "catch-all" in
that it will always return the `Zend\View\Renderer\PhpRenderer` and populate
the Response body with the results of rendering.
- `Zend\View\Strategy\JsonStrategy`. This strategy inspects the `Accept` HTTP
request header, if present, and determines if the client has indicated it
accepts an `application/json` response. If so, it will return the
- `Zend\View\Strategy\JsonStrategy`. This strategy will return the
`Zend\View\Renderer\JsonRenderer`, and populate the Response body with the
JSON value returned, as well as set a `Content-Type` header with a value of
`application/json`.
- `Zend\View\Strategy\FeedStrategy`. This strategy inspects the `Accept` HTTP
header, if present, and determines if the client has indicated it accepts
either an `application/rss+xml` or `application/atom+xml` response. If so, it
will return the `Zend\View\Renderer\FeedRenderer`, setting the feed type to
- `Zend\View\Strategy\FeedStrategy`. This strategy will return the
`Zend\View\Renderer\FeedRenderer`, setting the feed type to
either "rss" or "atom", based on what was matched. Its Response strategy will
populate the Response body with the generated feed, as well as set a
`Content-Type` header with the appropriate value based on feed type.
Expand All @@ -606,13 +604,15 @@ register the `JsonStrategy`:
```php
namespace Application;

use Zend\Mvc\MvcEvent;

class Module
{
/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function onBootstrap($e)
public function onBootstrap(MvcEvent $e)
{
// Register a "render" event, at high priority (so it executes prior
// to the view attempting to render)
Expand All @@ -621,10 +621,10 @@ class Module
}

/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function registerJsonStrategy($e)
public function registerJsonStrategy(MvcEvent $e)
{
$app = $e->getTarget();
$locator = $app->getServiceManager();
Expand All @@ -639,34 +639,64 @@ class Module

The above will register the `JsonStrategy` with the "render" event, such that it
executes prior to the `PhpRendererStrategy`, and thus ensure that a JSON payload
is created when requested.
is created when the controller returns a `JsonModel`.

You could also use the module configuration to add the strategies:
```php
namespace Application;

use Zend\ModuleManager\Feature\ConfigProviderInterface;

class Module implements ConfigProviderInterface
{
/**
* Returns configuration to merge with application configuration
*
* @return array
*/
public function getConfig()
{
return [
/* ... */
'view_manager' => [
/* ... */
'strategies' => [
'ViewJsonStrategy',
],
],
];
}
}
```

What if you want this to happen only in specific modules, or specific
controllers? One way is similar to the last example in the
[previous section on layouts](#dealing-with-layouts), where we detailed changing
the layout for a specific module:

```php
namespace Content;
namespace Application;

use Zend\Mvc\MvcEvent;

class Module
{
/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function onBootstrap($e)
public function onBootstrap(MvcEvent $e)
{
// Register a render event
$app = $e->getParam('application');
$app->getEventManager()->attach('render', [$this, 'registerJsonStrategy'], 100);
}

/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @param MvcEvent $e The MvcEvent instance
* @return void
*/
public function registerJsonStrategy($e)
public function registerJsonStrategy(MvcEvent $e)
{
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
Expand Down Expand Up @@ -694,124 +724,51 @@ class Module
While the above examples detail using the `JsonStrategy`, the same could be done
for the `FeedStrategy`.

What if you want to use a custom renderer? Or if your app might allow a
combination of JSON, Atom feeds, and HTML? At this point, you'll need to create
your own custom strategies. Below is an example that appropriately loops through
the HTTP `Accept` header, and selects the appropriate Renderer based on what is
matched first.

If you successfully registered the Strategy you need to use the appropriate `ViewModel`:
```php
namespace Content\View;
namespace Application;

use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\ListenerAggregateTrait;
use Zend\Feed\Writer\Feed;
use Zend\View\Renderer\FeedRenderer;
use Zend\View\Renderer\JsonRenderer;
use Zend\View\Renderer\PhpRenderer;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\View\Model\JsonModel;
use Zend\View\Model\FeedModel;

class AcceptStrategy implements ListenerAggregateInterface
class MyController extends AbstractActionController
{
use ListenerAggregateTrait;

protected $feedRenderer;
protected $jsonRenderer;
protected $phpRenderer;

public function __construct(
PhpRenderer $phpRenderer,
JsonRenderer $jsonRenderer,
FeedRenderer $feedRenderer
) {
$this->phpRenderer = $phpRenderer;
$this->jsonRenderer = $jsonRenderer;
$this->feedRenderer = $feedRenderer;
}

public function attach(EventManagerInterface $events, $priority = 1)
/**
* Lists the items as HTML
*/
public function listAction()
{
$this->listeners[] = $events->attach('renderer', [$this, 'selectRenderer'], $priority);
$this->listeners[] = $events->attach('response', [$this, 'injectResponse'], $priority);
$items = /* ... get items ... */;
$viewModel = new ViewModel();
$viewModel->setVariable('items', $items);
return $viewModel;
}

/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @return \Zend\View\Renderer\RendererInterface
* Lists the items as JSON
*/
public function selectRenderer($e)
public function listJsonAction()
{
$request = $e->getRequest();
$headers = $request->getHeaders();

// No Accept header? return PhpRenderer
if (!$headers->has('accept')) {
return $this->phpRenderer;
}

$accept = $headers->get('accept');
foreach ($accept->getPrioritized() as $mediaType) {
if (0 === strpos($mediaType, 'application/json')) {
return $this->jsonRenderer;
}

if (0 === strpos($mediaType, 'application/rss+xml')) {
$this->feedRenderer->setFeedType('rss');
return $this->feedRenderer;
}

if (0 === strpos($mediaType, 'application/atom+xml')) {
$this->feedRenderer->setFeedType('atom');
return $this->feedRenderer;
}
}

// Nothing matched; return PhpRenderer. Technically, we should probably
// return an HTTP 415 Unsupported response.
return $this->phpRenderer;
$items = /* ... get items ... */;
$viewModel = new JsonModel();
$viewModel->setVariable('items', $items);
return $viewModel;
}

/**
* @param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* @return void
* Lists the items as a Feed
*/
public function injectResponse($e)
public function listFeedAction()
{
$renderer = $e->getRenderer();
$response = $e->getResponse();
$result = $e->getResult();

if ($renderer === $this->jsonRenderer) {
// JSON Renderer; set content-type header
$headers = $response->getHeaders();
$headers->addHeaderLine('content-type', 'application/json');
$response->setContent($result);
return
}

if ($renderer === $this->feedRenderer) {
// Feed Renderer; set content-type header, and export the feed if
// necessary
$feedType = $this->feedRenderer->getFeedType();
$headers = $response->getHeaders();
$mediatype = 'application/'
. (('rss' == $feedType) ? 'rss' : 'atom')
. '+xml';
$headers->addHeaderLine('content-type', $mediatype);

// If the $result is a feed, export it
if ($result instanceof Feed) {
$result = $result->export($feedType);
}

$response->setContent($result);
return;
}
$items = /* ... get items ... */;
$viewModel = new FeedModel();
$viewModel->setVariable('items', $items);
return $viewModel;
}
}
```

This strategy would be registered just as we demonstrated registering the
`JsonStrategy` earlier. You would also need to define service manager
configuration to ensure the various renderers are injected when you retrieve the
strategy from the application's locator instance.
Or you could switch the `ViewModel` dynamically based on the "Accept" HTTP Header with the
[Zend-Mvc-Plugin AcceptableViewModelSelector](http://zendframework.github.io/zend-mvc/plugins/#acceptableviewmodelselector-plugin).
3 changes: 1 addition & 2 deletions src/Strategy/FeedStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ public function attach(EventManagerInterface $events, $priority = 1)
}

/**
* Detect if we should use the FeedRenderer based on model type and/or
* Accept header
* Detect if we should use the FeedRenderer based on model type
*
* @param ViewEvent $e
* @return null|FeedRenderer
Expand Down
3 changes: 1 addition & 2 deletions src/Strategy/JsonStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ public function getCharset()
}

/**
* Detect if we should use the JsonRenderer based on model type and/or
* Accept header
* Detect if we should use the JsonRenderer based on model type
*
* @param ViewEvent $e
* @return null|JsonRenderer
Expand Down