Neurons to bytes

Блоки в Zend Framework. Финальная версия.

Я продолжил свои изыскания на тему блоков в Zend Framework. Я все искал, как сделать работу с блоками на сайте максимально удобной.

До этого, я написал несколько статей, посвященных блокам:

Но не далее, как неделю назад я придумал еще более удобный способ работы с блоками. Некоторые мои предыдущие утверждения я опровергнул :)

Блоки в новой версии так же работают на основе конфига блоков. Но теперь блоки можно как включать, так и отключать в любом месте контроллера. Именно отключения блоков и не было в прошлых версиях - как мне тогда показалось, оно было излишним. Но практика показала, что это не так.

Итак, в этой версии у нас есть помощник действия (action helper) и плагин фронт-контроллера (controller plugin). При помощи помощника действия мы указываем, какие блоки нужно будет загрузить или отключить, а плагин фронт-контроллера управляет непосредственно загрузкой блоков, которая происходит сразу после диспетчеризации действия в контроллере.

Сначала приведу листинг конфига блоков. Например, у нас будет конфиг с двумя определенными блоками:

[toolbar]
  module=application
  controller=toolbar
  action=index

[banner]
  module=application
  controller=banner
  action=show

Дальше, давайте посмотрим на код помощника действия:

/**
 * @author http://torqueo.net/
 */
class Project_Controller_Action_Helper_Block extends Zend_Controller_Action_Helper_Abstract
{
    /**
     * An instance of Project_Controller_Plugin_Block plugin
     * @var object
     */
    protected $_blocksPlugin = null;

    /**
     * Initialize Project_Controller_Plugin_Block plugin
     * and blocks config
     */
    public function init()
    {
        $front = Zend_Controller_Front::getInstance();
        if (!$front->hasPlugin('Project_Controller_Plugin_Block')) {
            $this->_blocksPlugin = new Project_Controller_Plugin_Block();
            $front->registerPlugin($this->_blocksPlugin, 93);
        } else {
            $this->_blocksPlugin = $front->getPlugin('Project_Controller_Plugin_Block');
        }

        $blocksConfig = new Zend_Config_Ini(APPLICATION_PATH . "/config/blocks.ini");
        $this->_blocksPlugin->setConfig( $blocksConfig );
    }

    /**
     * Add block on page by block name
     * @param string $blockName
     */
    public function add($blockName)
    {
        $this->_blocksPlugin->add($blockName);
    }

    /**
     * Remove block from page by block name
     * @param string $blockName
     */
    public function remove($blockName)
    {
        $this->_blocksPlugin->remove($blockName);
    }

    /**
     * Remove all blocks from page
     */
    public function removeAll()
    {
        $this->_blocksPlugin->removeAll();
    }
}

Теперь взглянем, как выглядит плагин фронт-контроллера, отвечающий за загрузку блоков:

/**
 * @author http://torqueo.net/
 */
class Project_Controller_Plugin_Block extends Zend_Controller_Plugin_Abstract
{
    /**
     * Here's a states for each block (enabled or disabled)
     * @var array
     */
    protected $_blocks  = array();

    /**
     * Blocks config with specified module, controller
     * and action for each block
     * @var array
     */
    protected $_config = array();

    /**
     * Enable block by block name  (set enabled flags)
     * @param string $blockName
     * @return void
     */
    public function add($blockName)
    {
        $this->_blocks[ $blockName ] = true;
    }

    /**
     * Disable block by block name (set disabled flags)
     * @param string $blockName
     * @return void
     */
    public function remove($blockName)
    {
        $this->_blocks[ $blockName ] = false;
    }

    /**
     * Disable all blocks (set disabled flags to all)
     * @param string $blockName
     * @return void
     */
    public function removeAll()
    {
        foreach ($this->_blocks as $blockName=>$blockEnabled) {
            $this->_blocks[ $blockName ] = false;
        }
    }

    /**
     * Hook on postDispatch event. Disable or enable blocks.
     * @param Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function postDispatch(Zend_Controller_Request_Abstract $request)
    {
        if (!$request->isXmlHttpRequest() && !empty($this->_config)) {
            $this->_layout = Zend_Layout::getMvcInstance();
            $this->_view   = $this->_layout->getView();

            foreach ($this->_config as $blockName=>$options) {
                if (isset($this->_blocks[ $blockName ]) && $this->_blocks[ $blockName ] === true) {
                    $this->_layout->{$blockName} = $this->_view->action(
                        $options['action'],
                        $options['controller'],
                        $options['module'],
                        $request->getParams()
                    );
                }
            }

        }
    }

    /**
     * Set blocks config
     * @param Zend_Config $config
     * @return void
     */
    public function setConfig(Zend_Config $config)
    {
        $this->_config = $config->toArray();
    }
}

В итоге в любом контроллере нашего приложения мы можем работать с блоками вот так:

class NewsController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // Add toolbar block on page
        $this->_helper->block->add('toolbar');

        // Add banners block on page
        $this->_helper->block->add('banner');
    }

    public function viewNews()
    {
        // Disable banners block in this action
        $this->_helper->block->remove('banner');

        // ... or disable all blocks
        $this->_helper->block->removeAll();
    }
}

В макете (layout) любой блок можно вывести так, по имени блока:

<?=$this->layout()->toolbar?>

<?=$this->layout()->banner?>

По комметариям в коде должно быть понятно, как нужно работать с блоками. Можно еще добавить в помощник метод addAll() для загрузки всех блоков, в противоположность методу removeAll() - но это уже на ваш вкус.

Надеюсь, вам помогла эта заметка. Буду рад комментариям.

Tag after , , ,

  1. lcf says:

    Выглядит красиво.

  2. lcf says:

    А зачем в

    public function setConfig(Zend_Config $config)
    {
    $this->_config = $config->toArray();
    }

    переводить в массив?

    • Ouch! says:

      Вообще-то да, справедливое замечание. Zend_Config имеет итератор, я что-то про это совсем забыл. Так что можно не приводить к массиву, а бегать по объекту.

  3. Zh0rzh says:

    А почему здесь используется плагин фронт-контроллера?
    action-хелперы ведь тоже позволяют встроить postDispatch-событие.

    Тогда вместо двух - у вас будет один класс

    • Ouch! says:

      Плагин будет вызываться один раз, а хелпер столько раз, сколько контроллеров у нас запускается. Или я неправ?

  4. helloamigo says:

    =) Спасибо, как говорится, Слона-то я и не приметил ;)


(required)


(required but won't be displayed)