想法讨论:zend MVC实现中的动态视图脚本切换

时间:2011-03-06 12:32:08

标签: zend-framework zend-view zend-framework-mvc

这基本上就是“我做得对吗?”问题

我知道如何在运行时透明地切换默认/移动版本/管理区域的视图。我想知道你在这种方法中看到的利弊。

主要要求是:

  1. 切换整个应用程序 很少甚至没有编码
  2. 集成到zend MVC工作流程中, 不要覆盖它
  3. 回退到默认
  4. 保留标准功能
  5. 控制器不应该知道 变化
  6. 这是我的pseudohelper

    class Xrks_Controller_Action_Helper_VrExtension extends Zend_Controller_Action_Helper_Abstract
    {
        public function postDispatch()
        {
            if(!$this->_shouldRender()) {
                return; //just skip
            }
            try {
                $vr = $this->_getViewRenderer();
                $backupView = clone $vr->view;
                $this->_setBasePaths(); //set base path(s) through ViewRenderer::initView($path)
                $oldSpecArray = $this->_setVrPathSpecs(); //set VR view script path specs
                $vr->render();
                $vr->setNoRender(true); //disable renderer
    
            } catch(Zend_View_Exception $e) { //fallback to default viewscripts if view script file not found 
                $vr->setView($backupView); //restore view on error
    
            } catch(Exception $e) {
                $vr->setView($backupView); //restore view on error
                $this->_setVrPathSpecs($oldSpecArray); //restore script path spec
                throw $e;
            }
            $this->_setVrPathSpecs($oldSpecArray);//restore script path spec
        }
    
        /**
         * Same functionality as ViewRenderer helper _shouldRender method
         * @return boolean
         */
        protected function _shouldRender();
    
        /**
         * @return Zend_Controller_Action_Helper_ViewRenderer
         */
        protected function _getViewRenderer();
    
        /**
         * Sets viewRenderer path specifications
         *
         * @param array $spec if NULL uses $this->_viewRendererPathSpecs
         * @return array old path spec (0 => pathSpec, 1 => pathNoControllerSpec)
         */
        protected function _setVrPathSpecs(array $spec = NULL);
    }
    

    应该如何配置完全帮助程序并不重要,而且该部分已跳过

    以下是它应该如何工作的示例:
    $这 - > _setBasePaths();将视图基本路径设置为 application / views / default / application / views / admin /
    $这 - > _setVrPathSpecs();将路径规范设置为“:module /:controller /:action。:suffix

    所以对于foo-baz-bar,它将搜索到 1. application / views / admin / scripts / foo / baz / bar.phtml
    2. application / views / default / scripts / foo / baz / bar.phtml
    如果未找到视图脚本,则回退到默认的ViewRenderer:
    3. application / modules / foo / views / scripts / baz / bar.phtml

    如果我错过了什么,请提问

    <小时/> 更新:经过一些研究后,我决定使用动作帮助器根据变形器和指定变量的规范自动调整视图scriptPaths。我还修改了部分帮助程序,以便在请求其他模块的部分时注册scriptPaths。

    这是行动助手的粗略但有效的版本:

    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();
        /**
         * By default following vars available: baseDir, area, theme, module
         * @var string
         */
        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());
        }
    
        /**
         * Enter description here ...
         * @return Zend_Controller_Action_Helper_ViewRenderer
         */
        protected function _getViewRenderer()
        {
            if(!$this->_viewRenderer) {
                $this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
            }
            return $this->_viewRenderer;
        }
    
        /**
         * Should the ViewRenderer render a view script?
         *
         * @return boolean
         */
        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)));//last occurence more important than first
            // array('test', 'test2', 'test') => array('test2', 'test')
            // @todo rethink this code piece later. must be better solution
        }
    
        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);//more specific first (moduleDir prior to module key)
            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);
            }
        }
    
        /**
         * Get inflector
         *
         * @return Zend_Filter_Inflector
         */
        public function getInflector()
        {
            if (null === $this->_inflector) {
                $this->_inflector = new Zend_Filter_Inflector();
                $this->_inflector->setThrowTargetExceptionsOn(true);
                //setup default rules
                $this->_inflector->addRules(array(
                         ':baseDir' => array(),
                     ))
                     ->setTargetReference($this->_viewScriptPathSpec);
            }
            return $this->_inflector;
        }
    
        /**
         *
         * @return array
         */
        public function getPaths()
        {
            return $this->_basePaths;
        }
    
        public function getEnabled()
        {
            return $this->_enabled;
        }
    
        public function setEnabled($flag = TRUE)
        {
            $this->_enabled = (bool)$flag;
            return $this;
        }
    
        /**
         *
         * @todo add check for $pathVars keys and values validity
         * @param array $pathVars associative array
         * @param string $placement either append or prepend
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack
         */
        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;
        }
    
        /**
         *
         * @param array|Zend_Config $paths
         * @param string $placement either append or prepend
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack
         * @throws Xrks_Exception
         */
        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;
        }
    
        /**
         *
         * @param array $pathVars associative array
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack
         */
        public function setPath(array $pathVars)
        {
            $this->_basePaths = array();
            $this->addPath($pathVars);
            return $this;
        }
    
        /**
         *
         * @param array|Zend_Config $paths
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack
         * @throws Xrks_Exception
         */
        public function setPaths($paths)
        {
            $this->_basePaths = array();
            $this->addPaths($paths);
            return $this;
        }
    
        /**
         *
         * @param string $varName
         * @return string |NULL
         */
        public function getDefaultVar($varName)
        {
            if(key_exists($varName, $this->_defaults)) {
                return $this->_defaults[$varName];
            }
            return NULL;
        }
    
        /**
         * @param string $varName
         * @param string $value
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
         */
        public function setDefaultVar($varName, $value)
        {
            $this->_defaults[$varName] = (string)$value;
            return $this;
        }
    
        /**
         *
         * @param string $name
         * @return string |NULL
         */
        public function getVar($name, $defaults = false)
        {
            if(key_exists($name, $this->_vars)) {
                return $this->_vars[$name];
            }
            return $defaults ? $this->getDefaultVar($name) : NULL;
        }
    
        /**
         * @param string $varName
         * @param string $value
         * @return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
         */
        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; //just skip
            }
            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;
            }
        }
    }
    

1 个答案:

答案 0 :(得分:1)

我通常采用的方式:

  • 我注册了一个Layout插件,扩展了Zend_Layout_Controller_Plugin_Layout
  • 我使用preDispatch挂钩来确定我所在的模块,控制器和操作
  • 我根据上下文在布局和视图之间切换

对我而言,这是迄今为止最简单的方法。

GJ