Поговоримо про Zend_Navigation

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

Показувати буду на прикладі пустої аплікухи, тому для початку створю каркас проекту використовуючи Zend_Tool.

$ zf create project ./

1. Меню
Насамперед, з допомогою ресурсів (теж з’явилися в ZF 1.8) налаштую 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"

І запускаю:
Менюшка :)
Вуаля :). Бачимо готову, розгорнуту менюшку у якій активний пункт позначається як 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>

Дивлюсь, що вийшло:
Хлібні крихти
Класно :)?
Метод 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_Navigation
        Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl($acl);
        Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole($role);
 
        return $acl;
    }
 
    /*
     * Ініціалізуємо об'єкт навігатора і передаємо його в 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',
                // Ресурс для перевірки прав доступу
                'resource'      => 'mvc:users',
                // Привілей
                'privilege'     => 'index',
                // Тут я обгортаю текст _(), щоб його можна було витягнути gettext'ом
                '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
        $view->menu = $container;
 
        return $container;
    }
 
}
?>

Запускаю:
Меню для гостей
І бачу меню для гостей! А хлібні крихти пусті, тому що треба задати мінімальну глибину рівною нулю.

Підсумок
Взагалі Zend_Navigation дуже зручна і гнучка штука. А! Я ще забув сказати, що стандартні в’ю хелпери підтримують Zend_Translate – дуже зручно при створенні багатомовних сайтів.
Ніби все. Надіюсь ця стаття стане вам у пригоді. Зараз ще перекладу її на російську, щоб опублікувати на habrahabr.ru та zendframework.ru (дуже корисний ресурс!).

UPD. Тут Олександр Махомет підказує, що ось так робити

        // Передаємо контейнер в View
        $view->menu = $container;

не дуже зручно, бо можна забутися і затерти цю змінну в контролері.
Якщо на сайті тільки одне меню, то можна робити простіше:

        $view->navigation($container);

І виводити таким чином:

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

7 thoughts on “Поговоримо про Zend_Navigation

  1. Спасибі за цікаву статтю. Дуже радує, що українською.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>