Ранее в этой главе мы создали лэйаут, общий для всех страниц веб-сайта. Однако нужно сделать еще пару вещей, чтобы лэйаут стал полностью функционирующим. Если помните, шаблон лэйаута содержит панель навигации и навигационную цепочку. Но оба этих компонента интерфейса на данный момент "статичны", а их необходимо сделать более интерактивными.
Например, активный элемент панели навигации должен зависеть от действия контроллера, выполняемого в данный момент. А навигационная цепочка должна отображать путь к текущей просматриваемой странице. В этом разделе мы сделаем эти виджеты полностью готовыми для работы на сайте с помощью наших собственных помощников видов.
Типичный помощник вида - это PHP-класс, который наследуется от класса
Zend\View\Helper\AbstractHelper
, который в свою очередь реализует
интерфейс Zend\View\Helper\HelperInterface
(диаграмма наследования классов
представлена на рисунке 6.9).
Для начала реализуем класс помощника вида Menu
, который будет визуализировать
HTML-код навигационной панели. Класс Menu
будет содержать несколько методов, позволяющих
задать элементы меню в виде массива, определить активный пункт меню и отобразить меню (описание методов
см. в таблице 6.4)
Имя метода | Описание |
---|---|
__construct($items) |
Конструктор класса. |
setItems($items) |
Метод для задания пунктов меню. |
setActiveItemId($activeItemId) |
Метод для задания активного пункта меню. |
render() |
Визуализирует меню. |
renderItem($item) |
Визуализирует один пункт меню. |
Информация, описывающая один пункт меню будет представлена в виде массива, как показано ниже (например, у элемента Home будут ID, текстовая надпись и URL для гиперссылки):
[
'id' => 'home',
'label' => 'Home',
'link' => $this->url('home')
]
Помимо этого мы хотим добавить поддержку выпадающих меню в качестве элементов навигации. Например, в случае с выпадающим меню Support, содержащим подэлементы Documentation и Help, описание элемента будет выглядеть следующим образом:
[
'id' => 'support',
'label' => 'Support',
'dropdown' => [
[
'id' => 'documentation',
'label' => 'Documentation',
'link' => $this->url('doc', ['page'=>'contents'])
],
[
'id' => 'help',
'label' => 'Help',
'link' => $this->url('static', ['page'=>'help'])
]
]
]
Мы хотим поместить класс Menu
в пространство имен Application\View\Helper
. Поэтому, начнем с
создания файла Menu.php
в директории View/Helper под корневым каталогом модуля Application
(рисунок 6.10).
Почему мы размещаем класс помощника вида под корневым каталогом модуля?
Помощники видов (в отличие от шаблонов представления
.phtml
) хранятся под корневой директорией модуляsrc/
, потому что они являются обычными PHP-классами и должны быть разрешены (resolve) функцией автозагрузки. С другой стороны, шаблоны представления разрешаются специальным ZF3-классом, называемым разрешатель представления (view resolver), и по этой причине шаблоны представления хранятся под каталогом модуляview/
.
Затем напишем код класса Menu
:
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
// Этот класс помощника вида отображает панель меню.
class Menu extends AbstractHelper
{
// Массив пунктов меню.
protected $items = [];
// ID активного пункта.
protected $activeItemId = '';
// Конструктор.
public function __construct($items=[])
{
$this->items = $items;
}
// Задаем пункты меню.
public function setItems($items)
{
$this->items = $items;
}
// Задаем ID активных пунктов.
public function setActiveItemId($activeItemId)
{
$this->activeItemId = $activeItemId;
}
}
В этом фрагменте мы определили несколько private-полей для класса Menu
. Поле $items
(строка 10) - это массив, который будет хранить информацию об элементах меню; а поле
$activeItemId
- ID активного пункта меню. Этот пункт будет визуально выделен.
В строках 16-19 мы определили метод конструктора класса, который (опционально)
принимает массив элементов для инициализации меню. Другой способ инициализировать меню -
через метод setItems()
(строки 22-25). И наконец, метод setActiveItemId()
задает
ID текущего активного пункта меню.
Теперь давайте добавим метод render()
, который будет генерировать HTML-код для
всей панели навигации и вернет его в виде строки:
// Визуализация меню.
public function render()
{
if (count($this->items)==0)
return ''; // Do nothing if there are no items.
$result = '<nav class="navbar navbar-default" role="navigation">';
$result .= '<div class="navbar-header">';
$result .= '<button type="button" class="navbar-toggle" ';
$result .= 'data-toggle="collapse" data-target=".navbar-ex1-collapse">';
$result .= '<span class="sr-only">Toggle navigation</span>';
$result .= '<span class="icon-bar"></span>';
$result .= '<span class="icon-bar"></span>';
$result .= '<span class="icon-bar"></span>';
$result .= '</button>';
$result .= '</div>';
$result .= '<div class="collapse navbar-collapse navbar-ex1-collapse">';
$result .= '<ul class="nav navbar-nav">';
// Визуализация элементов
foreach ($this->items as $item) {
$result .= $this->renderItem($item);
}
$result .= '</ul>';
$result .= '</div>';
$result .= '</nav>';
return $result;
}
В представленном выше коде мы создаем HTML-разметку для компонента панели навигации Bootstrap.
Панель будет использовать стандартную тему и будет складной (то есть, адаптироваться к различным
размерам экрана). Также у нее не будет текста с названием компании в заголовке.
В строках 22-24 мы перебираем все пункты меню и визуализируем каждый с помощью метода renderItem()
.
После этого метод render()
возвращает получившийся HTML-код в виде строки.
В завершение создания класса Menu
, реализуем метод renderItem()
. Он будет создавать
HTML-код для одного пункта меню:
// Визуализирует элемент.
protected function renderItem($item)
{
$id = isset($item['id']) ? $item['id'] : '';
$isActive = ($id==$this->activeItemId);
$label = isset($item['label']) ? $item['label'] : '';
$result = '';
if(isset($item['dropdown'])) {
$dropdownItems = $item['dropdown'];
$result .= '<li class="dropdown ' . ($isActive?'active':'') . '">';
$result .= '<a href="#" class="dropdown-toggle" data-toggle="dropdown">';
$result .= $label . ' <b class="caret"></b>';
$result .= '</a>';
$result .= '<ul class="dropdown-menu">';
foreach ($dropdownItems as $item) {
$link = isset($item['link']) ? $item['link'] : '#';
$label = isset($item['label']) ? $item['label'] : '';
$result .= '<li>';
$result .= '<a href="'.$link.'">'.$label.'</a>';
$result .= '</li>';
}
$result .= '</ul>';
$result .= '</a>';
$result .= '</li>';
} else {
$link = isset($item['link']) ? $item['link'] : '#';
$result .= $isActive?'<li class="active">':'<li>';
$result .= '<a href="'.$link.'">'.$label.'</a>';
$result .= '</li>';
}
return $result;
}
Итак, выше, в коде метода renderItem()
мы проделали следующее.
Сначала мы проверили, является ли элемент выпадающим меню или обычным элементом (строка 10).
В первом случае мы проходим по всем пунктам выпадающего меню и визуализируем их по очереди (строки 21-28).
Строки 35-39 содержат код визуализации для второго случая.
Чтобы иметь возможность использовать помощник вида Menu
в шаблоне представления, нужно зарегистрировать
его в конфигурации. Для этого добавьте ключ view_helpers
в файл module.config.php:
<?php
return [
// ...
// Следующие строки регистрируют наши пользовательские классы
// помощников видов в менеджере плагинов.
'view_helpers' => [
'factories' => [
View\Helper\Menu::class => InvokableFactory::class,
],
'aliases' => [
'mainMenu' => View\Helper\Menu::class
]
],
];
В примере выше мы зарегистрировали наш класс Menu
как помощник вида mainMenu
.
Мы сможем обращаться к нему из любого шаблона представления.
<!-- Панель навигации -->
<?php
$this->mainMenu()->setItems([
[
'id' => 'home',
'label' => 'Home',
'link' => $this->url('home')
],
[
'id' => 'downloads',
'label' => 'Downloads',
'link' => $this->url("application", ['action'=>'downloads'])
],
[
'id' => 'support',
'label' => 'Support',
'dropdown' => [
[
'id' => 'documentation',
'label' => 'Documentation',
'link' => $this->url('doc', ['page'=>'contents'])
],
[
'id' => 'help',
'label' => 'Help',
'link' => $this->url('static', ['page'=>'help'])
]
]
],
[
'id' => 'about',
'label' => 'About',
'link' => $this->url('about')
],
]);
echo $this->mainMenu()->render();
?>
В этом фрагменте мы обращаемся к зарегистрированному ранее помощнику вида mainMenu
и
задаем элементы панели навигации с помощью метода setItems()
(строка 3).
В качестве параметра мы передаем методу массив элементов.
Затем мы визуализируем панель навигации с помощью метода render()
.
Для задания активного элемента панели навигации, можно вызвать метод setActiveItemId()
из любого шаблона представления. Например, добавьте следующий код в начало шаблона представления
страницы About (application/index/about.phtml).
<?php
$this->mainMenu()->setActiveItemId('about');
?>
Если вы теперь откроете страницу About в своем браузере, вы увидите, что
пункт About меню навигации выделен другим цветом. Чтобы должным образом
отобразить активный элемент, надо вызывать метод setActiveItemId()
для каждой
страницы, находящейся в панели навигации (Home, Downloads, Documentation и т.д.)
Можете посмотреть, как это сделано, в нашем примере Hello World.
Теперь, когда вы знаете, как реализовать помощник вида, давайте создадим еще один
для отображения навигационной цепочки. Он полностью аналогичен помощнику вида
Menu
, так что мы просто приведем код класса Breadcrumbs
целиком.
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
// Этот класс помощника вида визуализирует навигационную цепочку.
class Breadcrumbs extends AbstractHelper
{
// Массив элементов.
private $items = [];
// Конструктор.
public function __construct($items=[])
{
$this->items = $items;
}
// Задаем элементы.
public function setItems($items)
{
$this->items = $items;
}
// Визуализируем навигационную цепочку.
public function render()
{
if(count($this->items)==0)
return ''; // Ничего не делать, если элементов нет.
// Полученный HTML-код будет храниться в этой переменной
$result = '<ol class="breadcrumb">';
// Получаем количество элементов
$itemCount = count($this->items);
$itemNum = 1; // счетчик элементов
// Проходим по элементам
foreach ($this->items as $label=>$link) {
// Делаем последний элемент неактивным
$isActive = ($itemNum==$itemCount?true:false);
// Визуализируем текущий элемент
$result .= $this->renderItem($label, $link, $isActive);
// Инкрементируем счетчик элементов
$itemNum++;
}
$result .= '</ol>';
return $result;
}
// Визуализируем элемент.
protected function renderItem($label, $link, $isActive)
{
$result = $isActive?'<li class="active">':'<li>';
if(!$isActive)
$result .= '<a href="'.$link.'">'.$label.'</a>';
else
$result .= $label;
$result .= '</li>';
return $result;
}
}
Чтобы иметь возможность использовать помощник вида Breadcrumbs
, нужно зарегистрировать
его в файле module.config.php следующим образом:
<?php
return [
//...
// The following registers our custom view helper classes.
'view_helpers' => [
'factories' => [
View\Helper\Breadcrumbs::class => InvokableFactory::class,
],
'aliases' => [
'pageBreadcrumbs' => View\Helper\Breadcrumbs::class,
]
],
];
Так как мы планируем использовать помощник вида Breadcrumbs
в шаблоне представления лэйаута,
замените разметку навигационной цепочки в файле layout.phtml следующим кодом:
<!-- Навигационная цепочка -->
<?= $this->pageBreadcrumbs()->render(); ?>
В этих строках мы обращаемся к помощнику вида pageBreadcrumbs()
и вызываем
его методом render()
. Оператор echo
затем выводит HTML-код навигационной
цепочки.
После этого нужно передать элементы навигационной цепочки для каждого шаблона представления. Например, для страницы About добавьте следующий фрагмент кода в шаблоне представления.
<?php
$this->pageBreadcrumbs()->setItems([
'Home'=>$this->url('home'),
'About'=>$this->url('about'),
]);
?>
Теперь при открытии страницы вы увидите навигационную цепочку как на рисунке 6.11. Пользователям сайта будет видно, на какой странице они находятся в данный момент, и они не смогут заблудиться.