Помощники вида в Zend Framework
|
Опубликовано: 09.11.2008
|
- От переводчика
- Вступление
- Что такое помощник вида
- Создание и регистрация помощников вида
- Размышления о путях
- Стандартные помощники вида
- Partials помощники
- Placeholder помощники
- Doctype
- Захват содержимого
- Агрегирование содержимого
- Особые случаи использования placeholder помощников
- Заключительное слово
Данный текст - перевод статьи , автором которой является . Оригинал статьи был опубликован 28 апреля 2008 года. Статья написана по Zend Framework версии 1.5, но сохраняет актуальность и для последующих релизов. На текущий момент последний стабильный релиз Zend Framework версии 1.6.2. В нем существует несколько новых стандартных помощников не упомянутых в статье, это: HtmlElement, HtmlFlash, HtmlObject, HtmlPage, HtmlQuicktime, PaginationControl, RenderToPlaceholder.
Некоторые особенности перевода:
View - вид
Model - модель
view scripts - скрипты вида
view helpers - помощники вида
layout - макет
content - содержимое, данные, информация (чаще всего имеется ввиду html код)
Согласно архитектуре MVC (Model-View-Controller) вид может получать доступ к модели (операции на чтение), а также выполнять некоторую логику отображения. Возникает вопрос, каким образом обращаться к модели? А что если у вас есть достаточно сложная логика, которую, например, вы захотите повторить? А возможно вы не хотите хранить ее непосредственно в скриптах вида, с целью сохранить ее читабельность? Для решения этих проблем Zend Framework предлагает помощников вида.
Помощник вида это класс, который следует определенной конвенции наименования. После того как вы зарегистрируете ваши помощники в объекте вида, вы сможете использовать их так, как будто это его обычные методы. Объект вида сохраняет объекты помощников, из чего следует, что сохраняются и состояния помощников.
Помощники вида обычно используются в следующих случаях:
- Доступ к моделям;
- Сложная или повторяемая логика отображения;
- Преобразование и форматирование данных модели;
- Сохранение данных между двумя скриптами вида.
Конвенция наименования помощников довольно простая. Имя класса должно заканчиваться именем файла в котором он описан, в MixedCase. Кроме этого, класс должен содержать метод с таким же именем как и окончание класса, но в camelCase. Для примера, если мы хотим назвать помощника именем 'FooBar', нам необходим класс, имя которого заканчивается на 'FooBar' и содержащий метод с именем 'fooBar'. Кроме этого есть еще одно правило. Классы помощников должны иметь префикс, они не могут называться также как файл в котором они содержатся (подробнее об этом позже). Мы дадим нашему помощнику префикс 'My_Helper'. В итоге наш хелпер должен выглядеть следующим образом:
class My_Helper_FooBar
{
public function fooBar()
{
}
}
Следуя стандартам Zend Framework, сохраним помощник в нашей библиотеке как My/Helper/FooBar.php. Теперь мы должны зарегистрировать помощник в объекте вида. Zend_View не разрешает регистрировать помощники по одному, но вы можете указать путь к директории с помощниками. Это можно сделать с помощью метода addHelperPath() который принимает в качестве аргументов путь $path и префикс $prefix:
$view->addHelperPath('My/Helper/', 'My_Helper');
Теперь вы можете вызвать ваш помощник, так как будто это родной метод Zend_View
$view->fooBar();
Теперь, предположим, что вы хотите работать с объектом вида или другими помощниками внутри вашего помощника. Для этого вы можете задать метод setView() в вашем помощнике, тогда Zend_View передаст помощнику текущий объект вида, после того как помощник будет создан. Давайте добавим эту функциональность:
class My_Helper_FooBar
{
public $view;
public function fooBar()
{
}
public function setView(Zend_View_Interface $view)
{
$this->view = $view;
}
}
Пришло время спросить себя, что же должен возвращать помощник. Следуя конвенции, помощники не должны непосредственно отображать результат работы, например, в браузер. Помощники должны возвращать результат, используя return. Большинство помощников возвращают контент (данные), но также могут возвращать экземпляры самих себя, что позволяет использовать дополнительные методы помощников. Это может быть полезно, например, когда у вас есть набор схожих задач, но вы при этом не хотите использовать несколько отдельных помощников. В конце концов, вы можете не возвращать ничего, выполняя определенные операции, например логирование.
Модифицируем наш помощник. Теперь он будет принимать аргумент – имя. Помощник сначала применит функцию escape объекта вида, а затем присоединив строку 'fooBar ' вернет результат. Помощник вышел не очень полезным, но зато иллюстрирует суть.
class My_Helper_FooBar
{
public $view;
public function fooBar($name)
{
return 'fooBar ' . $this->view->escape($name);
}
public function setView(Zend_View_Interface $view)
{
$this->view = $view;
}
}
Теперь поговорим немного о путях и префиксах помощников. Во-первых, путь и префикс класса не обязательно должны иметь взаимно однозначное соответствие, как это может быть в других случаях. Важно лишь то, что все классы в директории должны иметь этот префикс. Zend_View будет проверять существует ли файл с нужным названием помощника, если нет, продолжит поиск в следующей директории в стеке. Это означает, что вы можете группировать ваши помощники в одной директории или в нескольких. После того как вы зарегистрируете эти пути единожды, вы получите доступ ко всем помощникам.
Во-вторых, ранее было упомянуто, что каждый класс помощника должен иметь префикс. Это необходимо что бы избежать конфликта имен стандартных (поставляются вместе с компонентом Zend_View) и ваших собственных помощников. Также это позволяет наследовать существующие помощники для добавления новой функциональности (например, помощник partialLoop() наследует помощник partial())
Третье, основываясь на последнем утверждении, набор путей к помощникам видов, как и ко всем остальным плагинам в Zend Framework представляет собой стек LIFO(последний вошел, первый вышел). Это позволяет вам создавать ваши собственные помощники с вашими префиксами класса и таким образом «перезаписывать» помощники с такими же именами но другими префиксами классов. Другими словами, вы можете менять поведение стандартных помощников.
Пойдем дальше, предположим, вам не нравится, как работает помощник formHidden(). Вы хотите, что бы все элементы типа hidden содержали некоторый класс всегда, то есть вы не хотите его указывать вручную при создании элемента. Это может быть полезно если вы, например, хотите выполнить какую-нибудь операцию с помощью Javascript. Сделать это можно примерно следующим образом:
class My_View_Helper_FormHidden extends Zend_View_Helper_FormHidden
{
public function formHidden($name, $value = null, array $attribs = null)
{
if (null === $attribs) {
$attribs = array('class' => 'hidden');
} else {
if (array_key_exists('class', $attribs)) {
$attribs['class'] .= ' hidden';
} else {
$attribs['class'] = 'hidden';
}
}
return parent::formHidden($name, $value, $attribs);
}
}
Вы конечно должны не забыть добавить путь к помощнику в объект вида:
$view->addHelperPath('My/View/Helper/', 'My_View_Helper');
Теперь, всякий раз, когда вы будете вызывать помощник formHidden(), будет вызван уже не стандартный, а наш помощник.
Подведу итоги. Помощники это отличный инструмент для расширения функциональности Zend_View, а также прекрасный путь для настройки и расширения других помощников.
Теперь, когда мы научились создавать и регистрировать свои собственные помощники, рассмотрим стандартные помощники Zend Framework. Их, кстати, не так уж много.
Стандартные помощники поставляемые с Zend Framework 1.5 можно разделить на три категории
| Помощники форм | Placeholders | Вспомогательные |
| fieldset() form() formButton() formCheckbox() formErrors() formFile() formHidden() formImage() formLabel() formMultiCheckbox() formNote() formPassword() formRadio() formReset() formSelect() formSubmit() formText() formTextarea() htmlList() |
placeholder() doctype() headLink() headMeta() headScript() headStyle() headTitle() inlineScript() |
action() declareVars() json() layout() partial() partialLoop() translate() url() |
Я не хочу рассматривать подробно каждый помощник, большинство из них имеют «говорящие» названия, и все они описаны в мануале. Тем не менее, некоторые из них стоит рассмотреть.
У Zend_View есть метод render(), который собственно и позволяет «отображать» скрипты вида. При этом, каждый удачно отображенный скрипт вида будет содержать весь набор переменных что были переданы в объект вида (То есть, все переменные находятся в одной области видимости). Такое поведение может привести к проблеме, когда вы захотите повторно использовать скрипты на нескольких итерациях, или когда вы захотите управлять переменными вида (это может иметь последствия для последующих скриптов)
Здесь на сцену выходят помощники partials. Ключевое отличие между отображением partial и обычного скрипта вида в том что partial скрипты имеют свою собственную область видимости. Они будут видеть только те переменные, что были переданы непосредственно им. Пример
<?= $this->partial('foo.phtml', array('foo' => 'bar')) ?>
Если бы мы вызвали скрипт foo.phtml как обычно (render), он бы получил доступ ко всем переменным вида. Если же вызывать его как partial то он получит только одну переменную foo со значением bar.
В каких случаях это может пригодиться? Например, нам часто приходится работать с набором данных из модели. Случай такой распространенный, что для решения проблемы создан отдельный помощник partialLoop(). Этот помощник принимает набор данных, пробегает по всему набору и помещает каждый отдельный результат в индивидуальный partial скрипт
Предположим у нас есть объект Zend_Db_Table_Rowset, сохраненный в виде свойства нашего вида. Каждый результат в наборе это объект Zend_Db_Table_Row. Передавая Zend_Db_Table_Rowset в наш partial скрипт, мы получаем возможность работать с полями результата так, как будто это обычные переменные вида. Допустим, наш объект Zend_Db_Table_Row содержит поля 'username' и 'email', тогда мы создадим следующий partial скрипт.
<li>
<a href="mailto:<?= $this->escape($this->email) ?>"><?= $this->escape($this->username) ?></a>
</li>
Вызывать наш partial скрипт мы будем следующим образом
<ul>
<?= $this->partialLoop('results.phtml', $this->results) ?>
</ul>
Partials помощники позволяют нам создавать небольшие независимые части кода, которые мы можем повторно использовать в нашем приложении. Кроме того, в итоге, мы имеем более «чистые» содержащие меньше логики скрипты вида.
Обычно о placeholder помощниках вспоминают когда речь идет о работе двух разных скриптов вида с общими данными. Если вы используете MVC решение от Zend Framework, placeholder помощники возможно, вообще вам не пригодятся, ввиду того что вы можете использовать один объект вида и в контроллерах, и в макетах (layouts). То есть переменные вида позволяют решить проблему хранения данных. Но все же, когда placeholder помощники могут быть полезны?
Есть несколько вариантов ответа. Во-первых, не все используют единый объект вида, предпочитая создание индивидуального объекта для каждой задачи. Во-вторых, разработчики часто используют partial скрипты, имеющие свою область видимости. Единственный путь передать информацию из скрипта вида в скрипт partial это placeholder помощники (конечно, не забываем о варианте передачи переменных напрямую). Третье, некоторые placeholder-ы используются другими помощниками. Помощник doctype() является отличным примером такой ситуации. И наконец, placeholder помощники дают возможность агрегировать и захватывать данные для дальнейшего использования.
Поговорим об этом детальнее.
Помощник doctype() позволяет нам указывать HTML тег DocType. Этот помощник используют другие помощники, что бы определить какую разметку генерировать. Например, тег не должен иметь закрывающего тега по стандарту HTML4, тогда как по стандарту XHTML1 должен. Если вы создаете свой помощник и в нем хотите проверить используете ли вы HTML или XHMTL разметку, сделайте это следующим образом (Предполагается что объект вида доступен в вашем помощнике):
if ($this->view->doctype()->isXhtml()) {
}
Конечно, что бы такой код сработал, нам нужно заранее сообщить помощнику doctype какой DocType использовать. Если этого не сделать, будет использован DocType по умолчанию (HTML4 transitional). Добавьте следующий код в ваш загрузочный файл или в плагин (желательно, что бы он выполнялся пораньше.),
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->initView();
$viewRenderer->view->doctype('XHTML1_STRICT');
Вывести DocType в макете очень просто:
<?= $this->doctype() ?>
Часто необходимо сгенерировать данные в скрипте вида чтобы использовать их в другом месте макета. Как же одновременно сгенерировать данные и поместить их в placeholder
Ответ прост, placeholder помощники поддерживают функцию захвата. К примеру:
<? $this->placeholder('foo')->captureStart() ?>
Welcome, <a href="/user/profile/<?= $this->userId ?>">
<?= $this->username ?>
</a>
<? $this->placeholder('foo')->captureEnd() ?>
Вывести захваченные данные очень просто:
<?= $this->placeholder('foo') ?>
Хотя захват содержимого полезен сам по себе, в сочитаннии с агрегированием содержимого он полезен особенно.
Хранилище placeholder помощников наследует ArrayObject. Благодаря этому у нас появляются очень интересные и гибкие возможности, которых не было бы в случае хранения в виде простых значений.
В качестве примера, предположим, что мы разрабатываем боковую панель для нашего сайта. Панель будет содержать один или несколько блоков с данными. Панель должна быть обернута div элементом, а также содержать несколько div элементов внутри. Отображение блоков должно быть динамическое, и зависеть от статуса текущего пользователя, от страницы которую просматривают, и от активного раздела сайта.
Так как наши placeholder-ы это «продвинутые» массивы, добавление новых значений происходит очень легко. Это позволяет «сохранять» нашу панель между двумя скриптами вида и добавлять в нее новые элементы. Вдобавок мы имеем возможность указать содержимое, которое будет обрамлять и разделять наши значения агрегируемые в placeholder-ах.
Произведем первоначальную настройку нашей панели
<? $sidebar = $this->placeholder('sidebar');
$sidebar->setPrefix('<div id="sidebar"><div class="sidebar-item">')
->setSeparator('</div><div class="sidebar-item">')
->setPostfix('</div></div>');
?>
Вышеприведенный код создает placeholder 'sidebar'. Далее задается текст который будет началом (setPrefix) . Затем мы указываем разделитель (setSeparator) для блоков, и наконец, указываем закрывающий (setPostfix) текст.
Для большей красоты вы можете добавить отступ, он будет применен ко всем значениям, сохраненным в placeholder-е
Обратите внимание что все эти настройки можно сделать в любой момент до вывода placeholder-а
Теперь предположим мы хотим вывести блок отображающий статус пользователя. Если пользователь авторизован, мы отображаем его имя, в противном случае ссылку на страницу авторизации. Мы выполним это в плагине что запустится в dispatchLoopStartup():
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->initView();
$view = $viewRenderer->view;
if (Zend_Auth::hasIdentity()) {
$username = Zend_Auth::getIdentity()->username;
$content = 'Welcome back, <b>' . $view->escape($username) . '</b>';
} else {
$content = '<a href="/login">Login</a>';
}
$view->placeholder('sidebar')->append($content);
Далее мы захотим вывести в нашем скрипте вида набор ссылок. Мы захватим этот блок, и таким образом добавим его в нашу панель. По умолчанию захваченные данные добавляются в placeholder, именно это нам и нужно.
<? $this->placeholder('sidebar')->captureStart() ?>
<ul>
<li><a href="/foo">Foo</a></li>
<li><a href="/bar">Bar</a></li>
<li><a href="/baz">Baz</a></li>
</ul>
<? $this->placeholder('sidebar')->captureEnd() ?>
Выводим наш placeholder в макете:
<?= $this->placeholder('sidebar') ?>
Получаем следующий результат
<div id="sidebar"><div class="sidebar-item">
<a href="/login">Login</a>
</div><div class="sidebar-item">
<ul>
<li><a href="/foo">Foo</a></li>
<li><a href="/bar">Bar</a></li>
<li><a href="/baz">Baz</a></li>
</ul>
</div></div>
Что если вы хотите добавить значение в placeholder впереди всех или хотите присвоить ему определенный индекс. Нет проблем, используйте методы prepend() или offsetSet($index, $content).
Вместе с Zend Framework поставляются несколько специальных реализаций placeholder-ов. Все они поддерживают базовую функциональность placeholder-ов, но также расширяют ее. В основном они используются для предоставления данных для макета в виде агрегированных данных , это название страницы, скрипты и css стили.
Если вы хотите узнать больше, ознакомьтесь со следующими ссылками:
Помощники вида штука не сложная, но мощная. С помощью них вы можете сохранить ваши скрипты вида чистыми и управляемыми а также выделить и инкапсулировать часто используемую логику (DRY принцип). Если ранее вы не использовали помощников вида, обязательно попробуйте, вам понравится!
- Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
- Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.
Комментарии для того чтобы: высказать свое аргументированное мнение о статье, указать какие участки вызывают непонимание, что нужно исправить/улучшить, просто сказать спасибо.
Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

