読者です 読者をやめる 読者になる 読者になる

[zf2] リクエストの拡張子別にviewにも対応したテンプレートを適用する

http://d.hatena.ne.jp/shogo4405/20141109/1415519613 の内容を応用してjsonに対するリクエストあればControllerに対応するjsonをテンプレートを変更すること可能になります。
/:controller[/:action][.format]とルーティングした際に、/view/controller/action.p{format}のテンプレートを選択するサンプルは次の通りです。

サンプルコード

<?php

namespace Application\Controller;

use Zend\Mvc\MvcEvent;
use Zend\View\Renderer\PhpRenderer;
use Zend\EventManager\EventInterface;
use Zend\EventManager\EventManagerInterface;

final class MimeListener extends \Zend\EventManager\AbstractListenerAggregate
{
    private static $mimeTypeCriteria = [
        'atom' => 'application/atom+xml',
        'json' => 'application/json',
    ];

    public function attach(EventManagerInterface $e)
    {
        $this->listeners[] = $e->attach(MvcEvent::EVENT_DISPATCH, [$this, 'onDispatch']);
        $this->listeners[] = $e->attach(MvcEvent::EVENT_RENDER, [$this, 'onRender'], -10000);
    }

    public function onDispatch(MvcEvent $e)
    {
        $controller = $e->getTarget();
        $sm = $controller->getServiceLocator();

        $format = $e->getRouteMatch()->getParam('format', '');
        if (!array_key_exists($format, self::$mimeTypeCriteria)) {
            return;
        }

        $stack = $sm->get('ViewTemplatePathStack');
        $stack->setDefaultSuffix('p' . $format);
        $controller->layout('layout/empty');

        if ($e->getResponse()->getStatusCode() === 404) {
            $view = $e->getViewModel();
            $view->setTemplate('error/404.' . $format);
        }
    }

    public function onRender(MvcEvent $e)
    {
        $format = $e->getRouteMatch()->getParam('format', '');
        $headers = $e->getResponse()->getHeaders();

        if (!array_key_exists($format, self::$mimeTypeCriteria)) {
            return;
        }

        $headers->addHeaderLine('Content-Type', self::$mimeTypeCriteria[$format]);
    }
}
<?php
final class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $application = $e->getApplication();

        $moduleRouteListener = new ModuleRouteListener();
        $eventManager = $application->getEventManager();
        $serviceManager = $application->getServiceManager();

        $eventManager->attachAggregate(new MimeListener());
        $moduleRouteListener->attach($eventManager);
    }
}

何がうれしいかと言うと

都度、action毎に返すformatを見てごにょごにょしないといけないのをやらなくて済むようになります。

// 適用前
function hogeAction()
{
  $format = $this->params()->fromRoute('format', false);
  if ($format === 'json') {
      return new JsonModel();
  } else if ($format === 'atom') {
      return new FeedModel();
  }
  return new ViewModel();
}

// 適用後
function hogeAction() {
  return [
    'content' => 'hello world!!'
  ]
}