Блоки в Zend Framework. Финальная версия.
02/09/2009 13:08
Я продолжил свои изыскания на тему блоков в Zend Framework. Я все искал, как сделать работу с блоками на сайте максимально удобной.
До этого, я написал несколько статей, посвященных блокам:
- Меню администратора, или блоки в Zend Framework
- Блоки в Zend Framework
- Блоки в Zend Framework, версия 2
Но не далее, как неделю назад я придумал еще более удобный способ работы с блоками. Некоторые мои предыдущие утверждения я опровергнул :)
Блоки в новой версии так же работают на основе конфига блоков. Но теперь блоки можно как включать, так и отключать в любом месте контроллера. Именно отключения блоков и не было в прошлых версиях - как мне тогда показалось, оно было излишним. Но практика показала, что это не так.
Итак, в этой версии у нас есть помощник действия (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() - но это уже на ваш вкус.
Надеюсь, вам помогла эта заметка. Буду рад комментариям.
Выглядит красиво.
А зачем в
public function setConfig(Zend_Config $config)
{
$this->_config = $config->toArray();
}
переводить в массив?
Вообще-то да, справедливое замечание. Zend_Config имеет итератор, я что-то про это совсем забыл. Так что можно не приводить к массиву, а бегать по объекту.
А почему здесь используется плагин фронт-контроллера?
action-хелперы ведь тоже позволяют встроить postDispatch-событие.
Тогда вместо двух - у вас будет один класс
Плагин будет вызываться один раз, а хелпер столько раз, сколько контроллеров у нас запускается. Или я неправ?
=) Спасибо, как говорится, Слона-то я и не приметил ;)