This is basically "Am I doing it right?" question.
I have an idea how I can transparently switch views for default areas / mobile version / admin at runtime. And I would like to know what pros and cons you see in this approach.
Primary requirements:
- switch the whole application with little encoded
- integrates into zend MVC workflow, do not overwrite it
- return to default
- keep standard functionality controllers
- don't have to know about changes
Here is my pseudo holper
class Xrks_Controller_Action_Helper_VrExtension extends Zend_Controller_Action_Helper_Abstract
{
public function postDispatch()
{
if(!$this->_shouldRender()) {
return;
}
try {
$vr = $this->_getViewRenderer();
$backupView = clone $vr->view;
$this->_setBasePaths();
$oldSpecArray = $this->_setVrPathSpecs();
$vr->render();
$vr->setNoRender(true);
} catch(Zend_View_Exception $e) {
$vr->setView($backupView);
} catch(Exception $e) {
$vr->setView($backupView);
$this->_setVrPathSpecs($oldSpecArray);
throw $e;
}
$this->_setVrPathSpecs($oldSpecArray);
}
protected function _shouldRender();
protected function _getViewRenderer();
protected function _setVrPathSpecs(array $spec = NULL);
}
How to fine-tune the assistant, it doesn’t matter, and this part missed
, :
$ → _ setBasePaths(); // / /views/admin/
$ → _ setVrPathSpecs(); ': module/: controller/: action.: suffix'
foo-baz-bar
1. /views/admin/scripts/foo/baz/bar.phtml
2. application/views/default/scripts/foo/baz/bar.phtml
view script , ViewRenderer:
3. /modules/foo/views/scripts/baz/bar.phtml
, -
Update: after some research, I decided to use the action assistant to view autoregister scriptPaths based on the specification for the inflector and the given variables. I also modified the partial helpers to register scriptPaths if the partial request is from another module.This is a crude but working version of the action helper:
class Xrks_Controller_Action_Helper_ViewRendererPathstack extends Zend_Controller_Action_Helper_Abstract
{
const PATH_APPEND = 'append';
const PATH_PREPEND = 'prepend';
protected $_enabled = FALSE;
protected $_viewScriptPaths = array();
protected $_viewScriptPathSpec = ':baseDir/:area/:module';
protected $_defaults = array(
'area' => 'frontend',
'theme' => 'default',
);
protected $_vars = array();
protected $_inflector;
protected $_viewRenderer;
public function __construct($baseDir = NULL)
{
if($baseDir == NULL) {
$baseDir = APPLICATION_PATH . DS . 'views';
}
$this->setDefaultVar('baseDir', $baseDir);
$this->addPath(array());
}
protected function _getViewRenderer()
{
if(!$this->_viewRenderer) {
$this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
}
return $this->_viewRenderer;
}
protected function _shouldRender()
{
$vR = $this->_getViewRenderer();
return (!$this->getFrontController()->getParam('noViewRenderer')
&& !$vR->getNeverRender()
&& !$vR->getNoRender()
&& (null !== $vR->getActionController())
&& $vR->getRequest()->isDispatched()
&& !$vR->getResponse()->isRedirect()
);
}
public function generatePaths(array $vars = array())
{
$this->_registerVarsWithInflector();
$vars = array_merge($this->_defaults, $this->_vars, $vars);
$inflector = $this->getInflector();
$generatedPaths = array();
foreach($this->_viewScriptPaths as $path) {
$pathVars = array_merge($vars, $path);
$generatedPaths[] = $inflector->filter($pathVars);
}
return array_reverse(array_unique(array_reverse($generatedPaths)));
}
protected function _registerVarsWithInflector()
{
$vars = array_merge($this->_defaults, $this->_vars);
$inflector = $this->getInflector();
$unregistered = array_keys(array_diff_key($vars, $inflector->getRules()));
sort($unregistered, SORT_DESC);
foreach($unregistered as $var) {
$inflector->addFilterRule($var, array('Word_CamelCaseToDash', 'StringToLower'));
}
}
protected function _viewAddScriptPaths(Zend_View_Abstract $view, $paths)
{
foreach ($paths as $path) {
$view->addScriptPath($path);
}
}
public function getInflector()
{
if (null === $this->_inflector) {
$this->_inflector = new Zend_Filter_Inflector();
$this->_inflector->setThrowTargetExceptionsOn(true);
$this->_inflector->addRules(array(
':baseDir' => array(),
))
->setTargetReference($this->_viewScriptPathSpec);
}
return $this->_inflector;
}
public function getPaths()
{
return $this->_basePaths;
}
public function getEnabled()
{
return $this->_enabled;
}
public function setEnabled($flag = TRUE)
{
$this->_enabled = (bool)$flag;
return $this;
}
public function addPath(array $pathVars, $placement = self::PATH_APPEND)
{
if($placement == self::PATH_PREPEND) {
array_unshift($this->_viewScriptPaths, $pathVars);
} else {
$this->_viewScriptPaths[] = $pathVars;
}
return $this;
}
public function addPaths($paths, $placement = self::PATH_APPEND)
{
if($paths instanceof Zend_Config) {
$paths = $paths->toArray();
} elseif (!is_array($paths)) {
throw new Xrks_Exception('$paths should be either array or instance of Zend_Config');
}
if($placement == self::PATH_PREPEND) {
$paths = array_reverse($paths);
}
foreach($paths as $path) {
$this->addPath((array)$path, $placement);
}
return $this;
}
public function setPath(array $pathVars)
{
$this->_basePaths = array();
$this->addPath($pathVars);
return $this;
}
public function setPaths($paths)
{
$this->_basePaths = array();
$this->addPaths($paths);
return $this;
}
public function getDefaultVar($varName)
{
if(key_exists($varName, $this->_defaults)) {
return $this->_defaults[$varName];
}
return NULL;
}
public function setDefaultVar($varName, $value)
{
$this->_defaults[$varName] = (string)$value;
return $this;
}
public function getVar($name, $defaults = false)
{
if(key_exists($name, $this->_vars)) {
return $this->_vars[$name];
}
return $defaults ? $this->getDefaultVar($name) : NULL;
}
public function setVar($varName, $value)
{
$this->_vars[$varName] = $value;
return $this;
}
public function unsetVar($name)
{
if(key_exists($name, $this->_vars)) {
unset($this->_vars[$name]);
}
return $this;
}
public function postDispatch()
{
if(!$this->getEnabled() || !$this->_shouldRender()) {
return;
}
try {
$vr = $this->_getViewRenderer();
$this->setVar('module', $vr->getModule());
$paths = $this->generatePaths();
$this->_viewAddScriptPaths($vr->view, $paths);
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($paths, Zend_Log::DEBUG);
}
} catch(Exception $e) {
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($e, Zend_Log::WARN);
}
throw $e;
}
}
}