framework.zend.com
Stable релиз 2.0 / 1.12

Поговорим о Zend_Navigation

К комментариям

Выход ZF 1.8 порадовал нас несколькими новыми (а главное очень полезными) компонентами. В этой статье я хочу рассказать о практике использования Zend_Navigation для построения меню сайта, карты сайта, хлебных крошек. Особое внимание уделю использованию Zend_Navigation в связке с Zend_Acl.

Это перевод статьи с моего блога. Самой полной версией всегда будет оригинал (на украинском).

Для начала создам каркас проекта используя Zend_Tool.

$ zf create project ./

1. Меню

Для настройки Zend_View добавляю в "application/configs/application.ini" следующий код:

; Views
resources.view.encoding = "UTF-8"
resources.view.basePath = APPLICATION_PATH "/views"
resources.view.helperPath.Application_View_Helper = APPLICATION_PATH "/views/helpers"

Дальше в файле "application/Bootstrap.php" создаю новый метод _initNavigation() (просьба читать комментарии в коде):

<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

/*
* Инициализируем объект навигатора и передаем его в View
*
* @return Zend_Navigation
*/
public function _initNavigation()
{
// Бутстрапим View
$this->bootstrapView();
$view = $this->getResource('view');

// Структура простого меню (можно вынести в XML)
$pages = array(
array(
'controller' => 'index',
'label' => _('Главная страница'),
),
array(
'controller' => 'users',
'action' => 'index',
// Я обворачиваю текст в _(), чтобы потом вытянуть его парсером gettext'а
'label' => _('Пользователи'),
'pages' => array (
array (
'controller' => 'users',
'action' => 'new',
'label' => _('Добавить пользователя'),
),
)
),
array (
'controller' => 'users',
'action' => 'registration',
'label' => _('Регистрация'),
),
array (
'controller' => 'users',
'action' => 'login',
'label' => _('Авторизация'),
),
array (
'controller' => 'users',
'action' => 'logout',
'label' => _('Выход'),
)
);

// Создаем новый контейнер на основе нашей структуры
$container = new Zend_Navigation($pages);
// Передаем контейнер в View
$view->menu = $container;

return $container;
}

}

?>

Если следовать здоровой логике, то меню должно присутствовать на всех (ну или большинстве) страниц сайта. Для этого идеально подходит Zend_Layout.

$ mkdir application/layouts
$ mkdir application/layouts/scripts
$ touch application/layouts/scripts/default.phtml

Добавляю в шаблон "application/layouts/scripts/default.phtml" вывод меню и контента страницы:
<div id="menu">
<h3>
<?php echo $this->translate('Меню'); ?>:
</h3>

<?php echo $this->navigation()->menu($this->menu); ?>
</div>

<div id="content">
<?php echo $this->layout()->content; ?>
</div>

А в "application/configs/application.ini" выношу настройки для ресурса layout, который инициализирует Zend_Layout:
; Layout
resources.layout.layout = "default"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

И запускаю:

zend_navigation меню

Вуаля :). Видим готовою, расхлопнутую менюшку! Активный пункт помечен как class="active".

2. Хлебные крошки

Ок, менюшка готова. Теперь я хочу, чтобы при пользовании сайтом пользователь всегда видел свое текущее месторасположение. Для этого можно использовать "хлебные крошки". Чтобы приложение не ругалось на отсутствие нужного контроллера или вьюшек я с помощью Zend_Tool создам контроллер Users добавлю к нему необходимые экшены. Все очень просто:

$ zf create controller users
$ zf create action new --controller-name users
$ zf create action registration --controller-name users
$ zf create action login --controller-name users
$ zf create action logout --controller-name users

Ну и добавлю немного нового кода в шаблон layout'a (между меню и контентом):

<div id="breadcrumbs">
<h3>
<?php echo $this->translate('Хлебные крошки'); ?>:
</h3>
<?php echo $this->navigation()->breadcrumbs($this->menu)->setLinkLast(true); ?>
</div>

Смотрю, что вышло:

zend_navigation меню

Прикольно :)? Метод setLinkLast(true) означает, что последнюю крошку нужно отображать как ссылку. Также можна указывать разделитель и минимальную глубину - смотрите API

3. Sitemap

С сайтмапом все так же просто. Все делается по аналогии. Вот мануал, а вот минимальный код:

<?php echo $this->navigation()->sitemap($this->menu); ?>

4. Zend_Navigation && Zend_Acl

А теперь я расскажу о том, что мне больше всего понравилось в Zend_Navigation более всего. О возможности использовать его в связке с Zend_Acl! Добавляю в Bootstrap роли и привилегии для доступа к страницам, а также инициализацию Zend_Acl (читайте комментарии к коду!):

<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

/**
* Инициализируем ACL.
* Создаем роли и ресурсы. Раздаем права доступа
*
* @return Zend_Acl
*/
protected function _initAcl()
{
$auth = Zend_Auth::getInstance();
// Определяем роль пользователя.
// Если не авторизирован - значит "гость"
$role = ($auth->hasIdentity() && !empty($auth->getIdentity()->role))
? $auth->getIdentity()->role : 'guest';

$acl = new Zend_Acl();

// Создаем роли
$acl->addRole(new Zend_Acl_Role('guest'))
->addRole(new Zend_Acl_Role('member'), 'guest')
->addRole(new Zend_Acl_Role('administrator'), 'member');

// Создаем ресурсы
// Я использую префиксы для наименования ресурсов
// "mvc:" - для страниц
$acl->add(new Zend_Acl_Resource('mvc:index'))
->add(new Zend_Acl_Resource('mvc:error'))
->add(new Zend_Acl_Resource('mvc:users'));

// Пускаем гостей на "морду" и на страницу ошибок
$acl->allow('guest', array('mvc:error', 'mvc:index'));

// А также на страницы авторизации и регистрации
$acl->allow('guest', 'mvc:users', array('login', 'registration'));
// А мемберам уже облом :)
$acl->deny('member', 'mvc:users', array('login', 'registration'));
// Ну и т.д.
$acl->allow('member', 'mvc:users', array('index', 'logout'));
$acl->allow('administrator', 'mvc:users', array('new'));

// Цепляем ACL к Zend_Navigator
Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl($acl);
Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole($role);

return $acl;
}

/*
* Инициализируем объект навигатора и передаем его в View
*
* @return Zend_Navigation
*/
public function _initNavigation()
{
$this->bootstrapView();
$view = $this->getResource('view');

$pages = array(
array(
'controller' => 'index',
'label' => _('Главная страница'),
),
array(
'controller' => 'users',
'action' => 'index',
// Ресурс для проверки прав доступа
'resource' => 'mvc:users',
// И привилегия
'privilege' => 'index',
'label' => _('Пользователи'),
'pages' => array (
array (
'controller' => 'users',
'action' => 'new',
'resource' => 'mvc:users',
'privilege' => 'new',
'label' => _('Добавить пользователя'),
),
)
),
array (
'controller' => 'users',
'action' => 'registration',
'resource' => 'mvc:users',
'privilege' => 'registration',
'label' => _('Регистрация'),
),
array (
'controller' => 'users',
'action' => 'login',
'resource' => 'mvc:users',
'privilege' => 'login',
'label' => _('Авторизация'),
),
array (
'controller' => 'users',
'action' => 'logout',
'resource' => 'mvc:users',
'privilege' => 'logout',
'label' => _('Выход'),
)
);

$container = new Zend_Navigation($pages);
$view->menu = $container;

return $container;
}

}

Запускаю:

zend_navigation меню

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

Итоги

Вобще Zend_Navigation очень удобный и гибкий компонент. Ещё я забыл сказать, что стандартные вью хелперы поддерживают Zend_Translate, что очень удобно при создании мультиязычных сайтов. Кажется все. Надеюсь эта статья вам пригодится.

UPD. Тут Александр Махомет подсказывает, что вот так делать

$view->menu = $container;

не очень удобно, потому что можно случайно затереть эту переменную в контроллере. Если на сайте только одно меню, тогда можно делать проще:

$view->navigation($container);

И выводить его таким образом:

<?php echo $this->navigation()->menu(); ?>
<?php echo $this->navigation()->breadcrumbs(); ?>
Лучший способ следить за обновлениями сайта это подписаться на RSS
Если информация была полезной для вас, вы можете поддержать сайт.
Комментарии:
LP 13.07.2009 09:18 #
буду благодарен если выложишь приисходники ;-)
Ответить
gw128 14.07.2009 11:10 #
Спасибо за отличный обзор. Очень вовремя появляются статьи.
Кстати мне тоже интересно насколько изменилась ваша сборка фреймворка по сравнению с исходниками из первых статей. Хотя теперь зенд всё упростили с выходом zf cli (zf create project).
Ответить
IgorN 20.07.2009 16:34 #
Интересный обзор. Кстати, а как там с расширением хлебных крошек? Например, хочу, что бы каждый элемент мог быть выпадающим списком...
Ответить
AndKul 20.07.2009 20:49 #
Столкнулся с тем что Zend_Navigation долго обрабатывает большие меню (каталог товаров интернет магазина), спасает только кеширование.
Ответить
Александр 29.07.2009 16:52 #
да все интересно но если бы глянуть исходники!!!
Ответить
Федор 03.09.2009 09:43 #
а как динамически добавить пункты подразделов из контроллера?
Ответить
Юрий 12.01.2010 22:39 #
Статья Интересная. Но есть вопросик, может кто сталкивался!
Я попытался сделать навигацию использую XML (Zend_Config_Xml)

<config>
<nav>
<logout>
<label>Выйти</label>
<controller>autorefication</controller>
<action>logout</action>
</logout>

<login>
<label>Вход</label>
<controller>autorefication</controller>
<action>login</action>
</login>


</nav>
</config>

и в браузер выводится Р’С…РѕРґ это название ссылки (т.е. сама ссылка создается, но ее название коверкается) Пытался изменить саму кодировку xml файла<?xml version="1.0" encoding="UTF-8"?> на <?xml version="1.0" encoding="WINDOWS-1251"?> и мета тег поставил на WINDOWS-1251, но ничего не помогает! Может кто сталкивался с такой проблемой или в Zend как-то можно решить проблуму с кодировкой
Ответить
Giz 19.02.2010 13:31 #
Кажется Zend_Navigation не работает с Windows-1251. На выходе $this->navigation()->menu() будет пусто на месте русских букв.

Хотелось задать еще один вопрос: как ставить свойство active для страницы в зависимости от HTTP-запроса? В Bootstrap это не получится сделать, так-как еще не идет процесс диспечеризации фронт-контроллера.
Ответить
Роман 17.10.2011 21:20 #
Ты нуб как и тот кто написал предведущей комментарий, пиши в кодировке ютф-8 сам файл. И всё заработает. в предведущем коменте тоже самое.
Ответить
Андрей 08.03.2010 00:12 #
до всего этого мне еще далековато, но есть один вопрос на другую тему, как можно создать меню на странице например через фото или картинки чтоб с нажатия каждого изображения попадал на ссылку по своему выбору???
Ответить
BT 24.03.2010 13:58 #
А не должна ли функция _InitNavigation быть protected вместо public?
Ответить
Александр Махомет 05.05.2010 11:15 #
Должна
Ответить
bonez 06.05.2010 14:16 #
огромная просьба - выложите исходники :)
Ответить
bonez 09.05.2010 19:39 #
у меня такая ситуация:

вывожу простое меню зенд навигатором. он подсвечивает мне тот пункт меню, в котором я нахожусь. но если я передам параметр, то пункт этот перестает подсвечиваться! на пример:

http://mysite.com/articles - подсвечивает
http://mysite.com/articles/100 - уже не подсвечивает

подскажите пожалуйста решение, а то замучился искать его :(
Ответить
amey 05.08.2010 09:40 #
У меня ругается на строку

'label'         => _('Главная страница'),

Fatal error: Call to undefined function _() in Z:\home\mytest\application\Bootstrap.php on line 33

В чем я ошибся?
Ответить
Александр Махомет 05.08.2010 10:19 #
Должно быть установлено расширение gettext
Ответить
amey 05.08.2010 12:43 #
Спасибо за быстрый ответ.
Ответить
Уважаемые пользователи. Комментарии не для того чтобы:
  1. Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
  2. Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.

Комментарии для того чтобы: высказать свое аргументированное мнение о статье, указать какие участки вызывают непонимание, что нужно исправить/улучшить, просто сказать спасибо.

Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

Комментарии не соответствующие этим правилам могут быть удалены. Спасибо за понимание.
Комментарии временно отключены, вы можете воспользоваться форумом.