From bd724faf5f49e278277fbc8150f473224dec24c1 Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Thu, 26 Apr 2018 11:44:17 -0400 Subject: [PATCH] Refactor class-based template-rendering to a shared base class. (#1172) --- .../AbstractClassBasedTemplateRenderer.php | 119 ++++++++++++++++++ .../src/VuFind/View/Helper/Root/Auth.php | 48 +------ .../src/VuFind/View/Helper/Root/Recommend.php | 40 +----- .../src/VuFind/View/Helper/Root/Record.php | 42 +------ .../src/VuFind/View/Helper/Root/Related.php | 40 +----- .../View/Helper/Root/RecordTest.php | 30 ++--- 6 files changed, 151 insertions(+), 168 deletions(-) create mode 100644 module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php b/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php new file mode 100644 index 00000000000..0d89815f9fe --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php @@ -0,0 +1,119 @@ +<?php +/** + * Abstract base class for helpers that render a template based on a class name. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Zend\View\Exception\RuntimeException; +use Zend\View\Helper\AbstractHelper; + +/** + * Authentication view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractClassBasedTemplateRenderer extends AbstractHelper +{ + /** + * Recursively locate and render a template that matches the provided class + * name (or one of its parent classes); throw an exception if no match is + * found. + * + * @param string $template Template path (with %s as class name placeholder) + * @param string $className Name of class to apply to template. + * @param string $topClassName Top-level parent class of $className (or null + * if $className is already the top level; used for recursion only). + * + * @return string + * @throws RuntimeException + */ + protected function resolveClassTemplate($template, $className, + $topClassName = null + ) { + // If the template resolves, we can render it! + $templateWithClass = sprintf($template, $this->getBriefClass($className)); + if ($this->getView()->resolver()->resolve($templateWithClass)) { + return $this->getView()->render($templateWithClass); + } + + // If the template doesn't resolve, let's see if we can inherit a + // template from a parent class: + $parentClass = get_parent_class($className); + if (empty($parentClass)) { + // No more parent classes left to try? Throw an exception! + throw new RuntimeException( + 'Cannot find ' . $templateWithClass . ' template for class: ' + . ($topClassName ?? $className) + ); + } + + // Recurse until we find a template or run out of parents... + return $this->resolveClassTemplate( + $template, $parentClass, $topClassName ?? $className + ); + } + + /** + * Render a template associated with the provided class name, applying to + * specified context variables. + * + * @param string $template Template path (with %s as class name placeholder) + * @param string $className Name of class to apply to template. + * @param array $context Context for rendering template + * + * @return string + */ + protected function renderClassTemplate($template, $className, $context = []) + { + // Set up the needed context in the view: + $contextHelper = $this->getView()->plugin('context'); + $oldContext = $contextHelper($this->getView())->apply($context); + + // Render the template for the current class: + $html = $this->resolveClassTemplate($template, $className); + + // Restore the original context before returning the result: + $contextHelper($this->getView())->restore($oldContext); + return $html; + } + + /** + * Helper to grab the end of the class name + * + * @param string $className Class name to abbreviate + * + * @return string + */ + protected function getBriefClass($className) + { + $classParts = explode('\\', $className); + return array_pop($classParts); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php index 901f5dffcda..137385bd357 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php @@ -28,7 +28,6 @@ namespace VuFind\View\Helper\Root; use VuFind\Exception\ILS as ILSException; -use Zend\View\Exception\RuntimeException; /** * Authentication view helper @@ -39,7 +38,7 @@ use Zend\View\Exception\RuntimeException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Auth extends \Zend\View\Helper\AbstractHelper +class Auth extends AbstractClassBasedTemplateRenderer { /** * Authentication manager @@ -80,37 +79,9 @@ class Auth extends \Zend\View\Helper\AbstractHelper { // Get the current auth module's class name $className = $this->getManager()->getAuthClassForTemplateRendering(); - - // Set up the needed context in the view: - $contextHelper = $this->getView()->plugin('context'); + $template = 'Auth/%s/' . $name; $context['topClass'] = $this->getBriefClass($className); - $oldContext = $contextHelper($this->getView())->apply($context); - - // Start a loop in case we need to use a parent class' name to find the - // appropriate template. - $topClassName = $className; // for error message - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $template = 'Auth/' . $this->getBriefClass($className) . '/' . $name; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find ' . $name . ' template for auth module: ' - . $topClassName - ); - } - } - } + return $this->renderClassTemplate($template, $className, $context); } /** @@ -196,19 +167,6 @@ class Auth extends \Zend\View\Helper\AbstractHelper return $this->renderTemplate('logindesc.phtml', $context); } - /** - * Helper to grab the end of the class name - * - * @param string $className Class name to abbreviate - * - * @return string - */ - protected function getBriefClass($className) - { - $classParts = explode('\\', $className); - return array_pop($classParts); - } - /** * Render the new password form template. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php b/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php index 72442eec473..e1f7dd3bf33 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php @@ -27,9 +27,6 @@ */ namespace VuFind\View\Helper\Root; -use Zend\View\Exception\RuntimeException; -use Zend\View\Helper\AbstractHelper; - /** * Recommendation module view helper * @@ -39,7 +36,7 @@ use Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Recommend extends AbstractHelper +class Recommend extends AbstractClassBasedTemplateRenderer { /** * Render the output of a recommendation module. @@ -51,38 +48,9 @@ class Recommend extends AbstractHelper */ public function __invoke($recommend) { - // Set up the rendering context: - $contextHelper = $this->getView()->plugin('context'); - $oldContext = $contextHelper($this->getView())->apply( - ['recommend' => $recommend] - ); - - // Get the current recommendation module's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'Recommend/%s.phtml'; $className = get_class($recommend); - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'Recommend/' . array_pop($classParts) . '.phtml'; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent recommendation class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find template for recommendation class: ' . - get_class($recommend) - ); - } - } - } + $context = ['recommend' => $recommend]; + return $this->renderClassTemplate($template, $className, $context); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Record.php b/module/VuFind/src/VuFind/View/Helper/Root/Record.php index cea7cb35a3a..036afbd262a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Record.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Record.php @@ -28,8 +28,6 @@ namespace VuFind\View\Helper\Root; use VuFind\Cover\Router as CoverRouter; -use Zend\View\Exception\RuntimeException; -use Zend\View\Helper\AbstractHelper; /** * Record driver view helper @@ -40,7 +38,7 @@ use Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Record extends AbstractHelper +class Record extends AbstractClassBasedTemplateRenderer { /** * Context view helper @@ -104,41 +102,11 @@ class Record extends AbstractHelper */ public function renderTemplate($name, $context = null) { - // Set default context if none provided: - if (null === $context) { - $context = ['driver' => $this->driver]; - } - - // Set up the needed context in the view: - $oldContext = $this->contextHelper->apply($context); - - // Get the current record driver's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'RecordDriver/%s/' . $name; $className = get_class($this->driver); - $resolver = $this->view->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'RecordDriver/' . array_pop($classParts) . '/' . $name; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->view->render($template); - $this->contextHelper->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find ' . $name . ' template for record driver: ' . - get_class($this->driver) - ); - } - } - } + return $this->renderClassTemplate( + $template, $className, $context ?? ['driver' => $this->driver] + ); } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Related.php b/module/VuFind/src/VuFind/View/Helper/Root/Related.php index 184d8457e91..a1c8203b6fe 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Related.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Related.php @@ -27,9 +27,6 @@ */ namespace VuFind\View\Helper\Root; -use Zend\View\Exception\RuntimeException; -use Zend\View\Helper\AbstractHelper; - /** * Related records view helper * @@ -39,7 +36,7 @@ use Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Related extends AbstractHelper +class Related extends AbstractClassBasedTemplateRenderer { /** * Plugin manager for related record modules. @@ -81,38 +78,9 @@ class Related extends AbstractHelper */ public function render($related) { - // Set up the rendering context: - $contextHelper = $this->getView()->plugin('context'); - $oldContext = $contextHelper($this->getView())->apply( - ['related' => $related] - ); - - // Get the current related item module's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'Related/%s.phtml'; $className = get_class($related); - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'Related/' . array_pop($classParts) . '.phtml'; - // Try to resolve the template.... - if ($resolver->resolve($template)) { - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find template for related items class: ' . - get_class($related) - ); - } - } - } + $context = ['related' => $related]; + return $this->renderClassTemplate($template, $className, $context); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php index b3f0715824a..7aa7e774ddb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php @@ -47,7 +47,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase * @return void * * @expectedException Zend\View\Exception\RuntimeException - * @expectedExceptionMessage Cannot find core.phtml template for record driver: VuFind\RecordDriver\SolrMarc + * @expectedExceptionMessage Cannot find RecordDriver/AbstractBase/core.phtml template for class: VuFind\RecordDriver\SolrMarc */ public function testMissingTemplate() { @@ -71,7 +71,9 @@ class RecordTest extends \PHPUnit\Framework\TestCase $record->getView()->resolver()->expects($this->at(0))->method('resolve') ->with($this->equalTo('RecordDriver/SolrMarc/collection-record.phtml')) ->will($this->returnValue(false)); - $this->setSuccessTemplate($record, 'RecordDriver/SolrDefault/collection-record.phtml', 'success', 1); + $this->setSuccessTemplate( + $record, 'RecordDriver/SolrDefault/collection-record.phtml', 'success', 1, 3 + ); $this->assertEquals('success', $record->getCollectionBriefRecord()); } @@ -183,7 +185,9 @@ class RecordTest extends \PHPUnit\Framework\TestCase // that one fail so we can load the parent class' template instead: $record->getView()->resolver()->expects($this->at(0))->method('resolve') ->will($this->returnValue(false)); - $this->setSuccessTemplate($record, 'RecordDriver/AbstractBase/list-entry.phtml', 'success', 1, 1); + $this->setSuccessTemplate( + $record, 'RecordDriver/AbstractBase/list-entry.phtml', 'success', 1, 3 + ); $this->assertEquals('success', $record->getListEntry(null, $user)); } @@ -492,21 +496,18 @@ class RecordTest extends \PHPUnit\Framework\TestCase if (null === $context) { $context = $this->getMockContext(); } - $view = $this->createMock('Zend\View\Renderer\PhpRenderer'); - if ($url) { - $url = $this->getMockUrl($url); - } - if (false !== $serverurl) { - $serverurl = $this->getMockServerUrl(); - } + $view = $this->getMockBuilder('Zend\View\Renderer\PhpRenderer') + ->disableOriginalConstructor() + ->setMethods(['render', 'plugin', 'resolver']) + ->getMock(); $pluginCallback = function ($helper) use ($context, $url, $serverurl) { switch ($helper) { case 'context': return $context; case 'serverurl': - return $serverurl; + return $serverurl ? $this->getMockServerUrl() : false; case 'url': - return $url; + return $url ? $this->getMockUrl($url) : $url; case 'searchTabs': return $this->getMockSearchTabs(); default: @@ -626,8 +627,9 @@ class RecordTest extends \PHPUnit\Framework\TestCase * * @return void */ - protected function setSuccessTemplate($record, $tpl, $response = 'success', $resolveAt = 0, $renderAt = 1) - { + protected function setSuccessTemplate($record, $tpl, $response = 'success', + $resolveAt = 0, $renderAt = 2 + ) { $expectResolve = $resolveAt === '*' ? $this->any() : $this->at($resolveAt); $record->getView()->resolver()->expects($expectResolve)->method('resolve') ->with($this->equalTo($tpl)) -- GitLab