Zend Framework 2: Disable layout
Thursday, February 13. 2014
This topic pops up every once in a while. As default every rendered page implicitly renders a layout too. This is especially bad for JSON or XML or binary responses your application may generate as a response. So far the best explanation and some helpful insights is in Mr. Chris Schreiber's blog scribles.com in the article Disabling Layout in Zend Framework 2.
I was working on a web application, which was a machine-to-machine app and not meant for humans at all. So a layout is completely unnecessary. As Chris instructs, I went directly to the module-class and copy/pasted his code. It failed. He has some sort of typo in the code:
$sharedEventManager->attach(
'Zend\Mvc\Controller\AbstractController',
function(\Zend\Mvc\MvcEvent $event) {
When I was looking for SharedEventManagerInterface PHP-code, it says:
/**
* Attach a listener to an event
*
* @param string|array $id Identifier(s) for event emitting
* @param string $event
* @param callable $callback PHP Callback
* @param int $priority Priority at which listener should
* @return void
*/
public function attach($id, $event, $callback, $priority = 1);
There clearly are 3 obligatory parameters. Chris' code has three parameters with the optional priority. Something is missing. This is the fix:
$sharedEventManager->attach(
MvcEvent::EVENT_DISPATCH,
'Zend\Mvc\Controller\AbstractController',
function(\Zend\Mvc\MvcEvent $event) {
Now it works! However, as my application was in its early stages, I was missing the default controller with the class name of IndexController. Adding the code into onBootstrap() didn't help. None of the callback's code was run during event dispatch. More debugging revealed, that my code never triggered the MvcEvent::EVENT_DISPATCH. It did trigger a MvcEvent::EVENT_DISPATCH_ERROR instead. The reason is obvious, I didn't have the class.
For clarity I'll copy/paste my onBootstrap() entirely here:
public function onBootstrap(MvcEvent $evt)
{
$eventManager = $evt->getApplication()->getEventManager();
$sharedEventManager = $eventManager->getSharedManager();
// Make sure layout is not rendered for regular pages
$sharedEventManager->attach('Zend\Mvc\Controller\AbstractController',
MvcEvent::EVENT_DISPATCH,
function (MvcEvent $event) {
$dispatchResult = $event->getResult();
if ($dispatchResult instanceof ViewModel) {
$dispatchResult->setTerminal(true);
}
}, -99
);
// Make sure layout is not rendered for HTTP/404 pages
$eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR,
function (MvcEvent $event) {
$dispatchResult = $event->getResult();
if ($dispatchResult instanceof ViewModel) {
$dispatchResult->setTerminal(true);
}
}, -99
);
}
Now both events are handled properly, without attempting to find a layout. To test my code, I added a layout/layout.phtml to the project and commented out my above code. It did renader the file contents. Then I removed the comments and deleted the layout/-directory completely. My code still ran on both occasions: when action can be found and when action cannot be found. Actually I also have a CLI-interface to the app, but that won't render the layout anyway.
This is yet another example of the complexity of ZF2. Any trivial task turns out to be a huge pain in the butt. I don't want to start ranting about using PhpRenderer in CLI-app, that's a completely another complaint.