Skip to content
This repository has been archived by the owner on May 31, 2023. It is now read-only.

Commit

Permalink
Add our own InjectTemplateListener
Browse files Browse the repository at this point in the history
  • Loading branch information
akrabat committed Dec 16, 2014
1 parent 9abcedd commit 577adab
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 0 deletions.
227 changes: 227 additions & 0 deletions module/RkaZf2Action/InjectTemplateListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php

namespace RkaZf2Action;

use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface as Events;
use Zend\Filter\Word\CamelCaseToDash as CamelCaseToDashFilter;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\ModuleRouteListener;
use Zend\View\Model\ModelInterface as ViewModel;

class InjectTemplateListener extends AbstractListenerAggregate
{
/**
* FilterInterface/inflector used to normalize names for use as template identifiers
*
* @var mixed
*/
protected $inflector;

/**
* Array of controller namespace -> template mappings
*
* @var array
*/
protected $controllerMap = array();

/**
* {@inheritDoc}
*/
public function attach(Events $events)
{
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'injectTemplate'), -90);
}

/**
* Inject a template into the view model, if none present
*
* Template is derived from the controller found in the route match, and,
* optionally, the action, if present.
*
* @param MvcEvent $e
* @return void
*/
public function injectTemplate(MvcEvent $e)
{
$model = $e->getResult();
if (!$model instanceof ViewModel) {
return;
}

$template = $model->getTemplate();
if (!empty($template)) {
return;
}

$routeMatch = $e->getRouteMatch();
$controller = $e->getTarget();
if (is_object($controller)) {
$controller = get_class($controller);
}
if (!$controller) {
$controller = $routeMatch->getParam('controller', '');
}

$template = $this->mapController($controller);
if (!$template) {
$module = $this->deriveModuleNamespace($controller);

if ($namespace = $routeMatch->getParam(ModuleRouteListener::MODULE_NAMESPACE)) {
$controllerSubNs = $this->deriveControllerSubNamespace($namespace);
if (!empty($controllerSubNs)) {
if (!empty($module)) {
$module .= '/' . $controllerSubNs;
} else {
$module = $controllerSubNs;
}
}
}

$controller = $this->deriveControllerClass($controller);
$template = $this->inflectName($module);

if (!empty($template)) {
$template .= '/';
}
$template .= $this->inflectName($controller);
}

$action = $routeMatch->getParam('action');
if (null !== $action) {
$template .= '/' . $this->inflectName($action);
}
$model->setTemplate($template);
}

/**
* Set map of controller namespace -> template pairs
*
* @param array $map
* @return self
*/
public function setControllerMap(array $map)
{
krsort($map);
$this->controllerMap = $map;
return $this;
}

/**
* Maps controller to template if controller namespace is whitelisted or mapped
*
* @param string $controller controller FQCN
* @return string|false template name or false if controller was not matched
*/
public function mapController($controller)
{
foreach ($this->controllerMap as $namespace => $replacement) {
if (
// Allow disabling rule by setting value to false since config
// merging have no feature to remove entries
false == $replacement
// Match full class or full namespace
|| !($controller === $namespace || strpos($controller, $namespace . '\\') === 0)
) {
continue;
}

$map = '';
// Map namespace to $replacement if its value is string
if (is_string($replacement)) {
$map = rtrim($replacement, '/') . '/';
$controller = substr($controller, strlen($namespace) + 1);
}

//strip Controller namespace(s) (but not classname)
$parts = explode('\\', $controller);
array_pop($parts);
$parts = array_diff($parts, array('Controller'));
//strip trailing Controller in class name
$parts[] = $this->deriveControllerClass($controller);
$controller = implode('/', $parts);

$template = trim($map . $controller, '/');

//inflect CamelCase to dash
return $this->inflectName($template);
}
return false;
}

/**
* Inflect a name to a normalized value
*
* @param string $name
* @return string
*/
protected function inflectName($name)
{
if (!$this->inflector) {
$this->inflector = new CamelCaseToDashFilter();
}
$name = $this->inflector->filter($name);
return strtolower($name);
}

/**
* Determine the top-level namespace of the controller
*
* @param string $controller
* @return string
*/
protected function deriveModuleNamespace($controller)
{
if (!strstr($controller, '\\')) {
return '';
}
$module = substr($controller, 0, strpos($controller, '\\'));
return $module;
}

/**
* @param $namespace
* @return string
*/
protected function deriveControllerSubNamespace($namespace)
{
if (!strstr($namespace, '\\')) {
return '';
}
$nsArray = explode('\\', $namespace);

// Remove the first two elements representing the module and controller directory.
$subNsArray = array_slice($nsArray, 2);
if (empty($subNsArray)) {
return '';
}
return implode('/', $subNsArray);
}

/**
* Determine the name of the controller
*
* Strip the namespace, and the suffix "Controller" if present.
*
* @param string $controller
* @return string
*/
protected function deriveControllerClass($controller)
{
if (strstr($controller, '\\')) {
$controller = substr($controller, strrpos($controller, '\\') + 1);
}

if ((10 < strlen($controller))
&& ('Controller' == substr($controller, -10))
) {
$controller = substr($controller, 0, -10);
} elseif ((6 < strlen($controller))
&& ('Action' == substr($controller, -6))
) {
$controller = substr($controller, 0, -6);
}

return $controller;
}
}
14 changes: 14 additions & 0 deletions module/RkaZf2Action/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,20 @@

class Module
{
public function getConfig()
{
// This works if https://github.com/zendframework/zf2/pull/7046 is merged
return [
'service_manager' => [
'invokables' => [
'InjectTemplateListener' => 'RkaZf2Action\InjectTemplateListener',
],
],
];
}
public function onBootstrap(MvcEvent $e)
{
// This is needed if https://github.com/zendframework/zf2/pull/7046 is not merged
$sharedEvents = $e->getApplication()->getEventManager()->getSharedManager();
$sharedEvents->attach('Zend\Stdlib\DispatchableInterface', 'dispatch', array($this, 'renameTemplate'), -91);
}
Expand All @@ -22,6 +34,8 @@ public function onBootstrap(MvcEvent $e)
* end of the template name. This listener removes the "-action" as it's from
* the Action class' name which we do not want to be in the template name.
*
* This is needed for Zend Framework < 2.4.0
*
* @param MvcEvent $e
* @return void
*/
Expand Down

0 comments on commit 577adab

Please sign in to comment.