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

Add controller namespace prefix to template mapping #5670

Merged
merged 3 commits into from Mar 4, 2014
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 73 additions & 15 deletions library/Zend/Mvc/View/Http/InjectTemplateListener.php
Expand Up @@ -25,6 +25,13 @@ class InjectTemplateListener extends AbstractListenerAggregate
*/
protected $inflector;

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

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -63,26 +70,29 @@ public function injectTemplate(MvcEvent $e)
$controller = $routeMatch->getParam('controller', '');
}

$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;
$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);
$controller = $this->deriveControllerClass($controller);
$template = $this->inflectName($module);

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

$action = $routeMatch->getParam('action');
if (null !== $action) {
Expand All @@ -91,6 +101,54 @@ public function injectTemplate(MvcEvent $e)
$model->setTemplate($template);
}

public function setControllerMap(array $map)
{
krsort($map);
$this->controllerMap = $map;
}

/**
* 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
*
Expand Down
11 changes: 10 additions & 1 deletion library/Zend/Mvc/View/Http/ViewManager.php
Expand Up @@ -122,8 +122,8 @@ public function onBootstrap($event)
$routeNotFoundStrategy = $this->getRouteNotFoundStrategy();
$exceptionStrategy = $this->getExceptionStrategy();
$mvcRenderingStrategy = $this->getMvcRenderingStrategy();
$injectTemplateListener = $this->getInjectTemplateListener();
$createViewModelListener = new CreateViewModelListener();
$injectTemplateListener = new InjectTemplateListener();
$injectViewModelListener = new InjectViewModelListener();

$this->registerMvcRenderingStrategies($events);
Expand Down Expand Up @@ -345,6 +345,15 @@ public function getRouteNotFoundStrategy()
return $this->routeNotFoundStrategy;
}

public function getInjectTemplateListener()
{
$listener = new InjectTemplateListener();
if (isset($this->config['controller_map'])) {
$listener->setControllerMap($this->config['controller_map']);
}
return $listener;
}

/**
* Configures the MvcEvent view model to ensure it has the template injected
*
Expand Down
128 changes: 128 additions & 0 deletions tests/ZendTest/Mvc/View/InjectTemplateListenerTest.php
Expand Up @@ -21,7 +21,12 @@ class InjectTemplateListenerTest extends TestCase
{
public function setUp()
{
$controllerMap = array(
'MappedNs' => true,
'ZendTest\MappedNs' => true,
);
$this->listener = new InjectTemplateListener();
$this->listener->setControllerMap($controllerMap);
$this->event = new MvcEvent();
$this->routeMatch = new RouteMatch(array());
$this->event->setRouteMatch($this->routeMatch);
Expand Down Expand Up @@ -125,6 +130,129 @@ public function testMapsSubNamespaceToSubDirectoryWithControllerFromEventTarget(
$this->assertEquals('zend-test/controller/test-asset/sample/test', $myViewModel->getTemplate());
}

public function testControllerMatchedByMapIsInflected()
{
$this->routeMatch->setParam('controller', 'MappedNs\SubNs\Controller\Sample');
$myViewModel = new ViewModel();

$this->event->setResult($myViewModel);
$this->listener->injectTemplate($this->event);

$this->assertEquals('mapped-ns/sub-ns/sample', $myViewModel->getTemplate());

$this->listener->setControllerMap(array('ZendTest' => true));
$myViewModel = new ViewModel();
$myController = new \ZendTest\Mvc\Controller\TestAsset\SampleController();
$this->event->setTarget($myController);
$this->event->setResult($myViewModel);

$this->listener->injectTemplate($this->event);

$this->assertEquals('zend-test/mvc/test-asset/sample', $myViewModel->getTemplate());
}

public function testControllerNotMatchedByMapIsNotAffected()
{
$this->routeMatch->setParam('action', 'test');
$myViewModel = new ViewModel();
$myController = new \ZendTest\Mvc\Controller\TestAsset\SampleController();

$this->event->setTarget($myController);
$this->event->setResult($myViewModel);
$this->listener->injectTemplate($this->event);

$this->assertEquals('zend-test/sample/test', $myViewModel->getTemplate());
}

public function testFullControllerNameMatchIsMapped()
{
$this->listener->setControllerMap(array(
'Foo\Bar\Controller\IndexController' => 'string-value',
));
$template = $this->listener->mapController('Foo\Bar\Controller\IndexController');
$this->assertEquals('string-value', $template);
}

public function testOnlyFullNamespaceMatchIsMapped()
{
$this->listener->setControllerMap(array(
'Foo' => 'foo-matched',
'Foo\Bar' => 'foo-bar-matched',
));
$template = $this->listener->mapController('Foo\BarBaz\Controller\IndexController');
$this->assertEquals('foo-matched/bar-baz/index', $template);
}

public function testControllerMapMatchedPrefixReplacedByStringValue()
{
$this->listener->setControllerMap(array(
'Foo\Bar' => 'string-value',
));
$template = $this->listener->mapController('Foo\Bar\Controller\IndexController');
$this->assertEquals('string-value/index', $template);
}

public function testUsingNamespaceRouteParameterGivesSameResultAsFullControllerParameter()
{
$this->routeMatch->setParam('controller', 'MappedNs\Foo\Controller\Bar\Baz\Sample');
$myViewModel = new ViewModel();

$this->event->setResult($myViewModel);
$this->listener->injectTemplate($this->event);

$template1 = $myViewModel->getTemplate();

$this->routeMatch->setParam(ModuleRouteListener::MODULE_NAMESPACE, 'MappedNs\Foo\Controller\Bar');
$this->routeMatch->setParam('controller', 'Baz\Sample');

$moduleRouteListener = new ModuleRouteListener;
$moduleRouteListener->onRoute($this->event);

$myViewModel = new ViewModel();

$this->event->setResult($myViewModel);
$this->listener->injectTemplate($this->event);

$this->assertEquals($template1, $myViewModel->getTemplate());
}

public function testControllerMapOnlyFullNamespaceMatches()
{
$this->listener->setControllerMap(array(
'Foo' => 'foo-matched',
'Foo\Bar' => 'foo-bar-matched',
));
$template = $this->listener->mapController('Foo\BarBaz\Controller\IndexController');
$this->assertEquals('foo-matched/bar-baz/index', $template);
}

public function testControllerMapRuleSetToFalseIsIgnored()
{
$this->listener->setControllerMap(array(
'Foo' => 'foo-matched',
'Foo\Bar' => false,
));
$template = $this->listener->mapController('Foo\Bar\Controller\IndexController');
$this->assertEquals('foo-matched/bar/index', $template);
}

public function testControllerMapMoreSpecificRuleMatchesFirst()
{
$this->listener->setControllerMap(array(
'Foo' => true,
'Foo\Bar' => 'bar/baz',
));
$template = $this->listener->mapController('Foo\Bar\Controller\IndexController');
$this->assertEquals('bar/baz/index', $template);

$this->listener->setControllerMap(array(
'Foo\Bar' => 'bar/baz',
'Foo' => true,
));
$template = $this->listener->mapController('Foo\Bar\Controller\IndexController');
$this->assertEquals('bar/baz/index', $template);
}

public function testAttachesListenerAtExpectedPriority()
{
$events = new EventManager();
Expand Down