Zend Framework 2 : Step by Step Create Custom View Strategy
Zend Framework 2 ships with three Rendering and Response Strategies that we can use within our application : PhpRendererStrategy, JsonStrategy, and FeedStrategy. If we want to use a custom strategy, for example : add content-type : image/png to all header response, we should create custom view strategy to create custom response. I will give you an example how to create it.
1. create a view strategy
namespace YourModule\View\Strategy; use Zend\EventManager\ListenerAggregateInterface; use Zend\EventManager\EventManagerInterface; use Zend\Mvc\MvcEvent; use Zend\View\Renderer\RendererInterface; use Zend\View\ViewEvent; use Zend\View\Model\ViewModel; class ImageStrategy implements ListenerAggregateInterface { protected $renderer; protected $listeners = array(); public function __construct(RendererInterface $renderer) { $this->renderer = $renderer; } public function attach(EventManagerInterface $events, $priority = 1) { $this->listeners[] = $events->attach(ViewEvent::EVENT_RENDERER, array($this, 'selectRenderer'), $priority); $this->listeners[] = $events->attach(ViewEvent::EVENT_RESPONSE, array($this, 'injectResponse'), $priority); } public function selectRenderer(ViewEvent $e) { return $this->renderer; } public function injectResponse(ViewEvent $e) { $renderer = $e->getRenderer(); if ($renderer !== $this->renderer) { return; } $result = $e->getResult(); $response = $e->getResponse(); $response->setContent($result); $headers = $response->getHeaders(); $headers->addHeaderLine('content-type', 'image/png'); } public function detach(EventManagerInterface $events) { foreach ($this->listeners as $index => $listener) { if ($events->detach($listener)) { unset($this->listeners[$index]); } } } }
2. Create Factory
namespace YourModule\View\Strategy; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class ImageFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { $viewRenderer = $serviceLocator->get('ViewRenderer'); return new ImageStrategy($viewRenderer); } }
3. Register in service manager
return array( 'factories' => array( 'ImageStrategy' => 'YourModule\View\Strategy\ImageFactory', ) );
4. Register in view_manager in your module.config.php
'view_manager' => array( 'strategies' => array( 'ImageStrategy' ), ),
Done !
What if we need the custom strategy is for specific module ? This is it :
namespace YourModule; use Zend\Mvc\MvcEvent; class Module { public function onBootstrap(MvcEvent $e) { $sharedEvents = $e->getApplication()->getEventManager()->getSharedManager(); $sm = $e->getApplication()->getServiceManager(); $sharedEvents->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) use ($sm) { $strategy = $sm->get('ImageStrategy'); $view = $sm->get('ViewManager')->getView(); $strategy->attach($view->getEventManager()); }, 100); } public function getServiceConfig() { return array( 'factories' => array( 'ImageStrategy' => 'YourModule\View\Strategy\ImageFactory', ) ); } public function getAutoloaderConfig(){ /*common code */ } public function getConfig(){ /*common code */ } }
Testing, read image file from your controller :
namespace YourModule\Controller; use Zend\Mvc\Controller\AbstractActionController; class SkeletonController extends AbstractActionController { public function fooAction() { return readfile('./public/images/zf2-logo.png'); } }
Reference :
1. http://zf2.readthedocs.org/en/latest/modules/zend.view.quick-start.html
2. http://juriansluiman.nl/en/article/119/attach-a-view-strategy-for-specific-modules
[…] https://samsonasik.wordpress.com/2012/12/10/zend-framework-2-step-by-step-create-custom-view-strategy… […]
Siiip…
offtopic: how can i get AuthenticationService in custom view helper because i get
__PHP_Incomplete_Class Object
setAuthService(new AuthenticationService);
if ($this->getAuthService()->hasIdentity()) {
$user = $this->getAuthService()->getIdentity();
print_r($user);
// $displayName = $user->getDisplayName();
// if (null === $displayName) {
// $displayName = $user->getUsername();
// }
// if (null === $displayName) {
// $displayName = $user->getEmail();
// $displayName = substr($displayName, 0, strpos($displayName, ‘@’));
// }
}
return $displayName;
}
public function getAuthService() {
return $this->authService;
}
public function setAuthService(AuthenticationService $authService) {
$this->authService = $authService;
return $this;
}
}
you should inject by service manager. create your view helper first.
Register into Service Manager.
Calling in view :
i use \DoctrineModule\Authentication\Adapter\ObjectRepository and i think the problem is when the object are serialize and saved to session, i extended that class and change object with array to solve the problem, but i will try your way to see if it’s working
Is there something like ZF1 AjaxContext which verify ‘X-Requested-With: XmlHttpRequest’ header?
read my prev article : https://samsonasik.wordpress.com/2012/12/02/zend-framework-2-disable-layout-in-specific-module/
Thanks, now i’ll swich viewmodel to jsonViewModel
You’re welcome 😉
Hello, I am struggling with setting xsl stylesheets to use for views. Do you have any resource where this would be explained? I tried using this blog and define a new strategy but it seems there’s much more to it. Thanks.
Additionaly, when trying to reproduce you png view strategy. I get following exception:
While attempting to create imagestrategy(alias: ImageStrategy) an invalid factory was registered for this instance type.
thrown at line where you assign $strategy in onBootsrap in Module:
$strategy = $sm->get(‘ImageStrategy’);
I could not resolve this problem. Thanks for any advice
add the following functions to your Module.php
one more thing, don’t forget to look at the name of file and name of namespace, adapt to your created module.
Thank you very much. I already had these 2 methods set up the same way as you suggested, but the problem was elsewhere, I have created the View subdir under src/ and not under src/Application so I had:
src/
Application/
Controller/
View/
I needed to move it to get
src/
Application/
Controller/
View/
The settings is obvious from namespace but I missed that and spent lot of time on this. Anyway, very good blog thanks and keep up.
white space removed, so I’m gonna try again:
wrong dir structure (if you want the code from this blog to work):
src/
–Application/
—-Controller/
–View/
correct:
src/
–Application/
—-Controller/
—-View/
[…] speziell den Listener an. Da wird auch der Content-type Header verändert. Musst du also anpassen. Zend Framework 2 : Step by Step Create Custom View Strategy | Welcome to Abdul Malik Ikhsan's Blog Hilft das weiter? Gruß, […]
[…] https://samsonasik.wordpress.com/2012/12/10/zend-framework-2-step-by-step-create-custom-view-strategy… […]
hi, i used the same concep to send emails with templates ,it works fine when is a http request but by console it cant render $serviceLocator->get(‘ViewRenderer’) , how can i solved
use Zend\View\Model\ViewModel instead. you can use $viewmodel->setTemplate() for it.
Thank you very much !
It works perfectly.
you’re welcome 😉
Thank you, it is the way to handle ActionControllers responses. I use it to output pdfstream.
I wanted to implement it like ZF, with a custom ViewModel. So I added a ViewRenderer implementation and a custom ViewModel working with the strategy. Now my ActionControllers are a way cleaner.
you’re welcome 😉
First – I have been referring this blog quite a bit. Thanks for sharing your knowledge !
My current goal :
I want to have context switching for a single action so that the data format varies depending on the request header.
My approach :
Using acceptableViewModelSelector, my action returns a different type of data (JSONModel vs ViewModel) depending on the acceptable request header.
I want to implement my own JSON Strategy(rather than use the inbuilt ZF2 one) using your given example.
However, the response now always returns a json even though the request is for text rather than json.
Any suggestion as to how I get the context switch to work ?
acceptableViewModelSelector should work, you need to check :
1. is ‘ViewJsonStrategy’ already added into ‘view_manager’ -> ‘strategies’ config ?
2. make sure your request using text/html header, you can check with curl.
or for text/html
3. make sure you added ‘text/html’ into your $acceptCriteria config like this :
Hello @samsonasik,
Thank you for this article and your blog in general. There is a lot of good information.
I wanted to ask you how I can return a Zend\Http\Response\Stream using the injectResponse from a Strategy ?
When I do $viewEvent->setResponse($myStream), the content never comes, my browser open its PDF reader (I populated the content-type header with application/pdf) but waits for it forever.
I posted an issue on github about that with more details (https://github.com/zendframework/zf2/issues/7568).
Thanks !