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

Помощники вида в Zend Framework

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

Содержание

От переводчика

Данный текст - перевод статьи View Helpers in Zend Framework, автором которой является Matthew Weier O'Phinney. Оригинал статьи был опубликован 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()


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

Partials помощники

У 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 помощники

Обычно о placeholder помощниках вспоминают когда речь идет о работе двух разных скриптов вида с общими данными. Если вы используете MVC решение от Zend Framework, placeholder помощники возможно, вообще вам не пригодятся, ввиду того что вы можете использовать один объект вида и в контроллерах, и в макетах (layouts). То есть переменные вида позволяют решить проблему хранения данных. Но все же, когда placeholder помощники могут быть полезны?

Есть несколько вариантов ответа. Во-первых, не все используют единый объект вида, предпочитая создание индивидуального объекта для каждой задачи. Во-вторых, разработчики часто используют partial скрипты, имеющие свою область видимости. Единственный путь передать информацию из скрипта вида в скрипт partial это placeholder помощники (конечно, не забываем о варианте передачи переменных напрямую). Третье, некоторые placeholder-ы используются другими помощниками. Помощник doctype() является отличным примером такой ситуации. И наконец, placeholder помощники дают возможность агрегировать и захватывать данные для дальнейшего использования.

Поговорим об этом детальнее.

Doctype

Помощник 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).

Особые случаи использования placeholder помощников

Вместе с Zend Framework поставляются несколько специальных реализаций placeholder-ов. Все они поддерживают базовую функциональность placeholder-ов, но также расширяют ее. В основном они используются для предоставления данных для макета в виде агрегированных данных , это название страницы, скрипты и css стили.

Если вы хотите узнать больше, ознакомьтесь со следующими ссылками:

Заключительное слово

Помощники вида штука не сложная, но мощная. С помощью них вы можете сохранить ваши скрипты вида чистыми и управляемыми а также выделить и инкапсулировать часто используемую логику (DRY принцип). Если ранее вы не использовали помощников вида, обязательно попробуйте, вам понравится!

Лучший способ следить за обновлениями сайта это подписаться на RSS
Если информация была полезной для вас, вы можете поддержать сайт.
Комментарии:
Andrey 12.11.2008 21:18 #
Огромное спасибо за статью, очень хорошая подборка материала с наглядными примерами
Ответить
Алексей 20.03.2009 17:09 #
Спасибо за статью. Продолжайте писать в таком же стиле. Очень полезно для начинающих! :)
Ответить
Вячеслав 23.05.2009 14:02 #
просто Большое Спасибо за Ваш труд. Действительно очень полезно для начинающих и не только.
Ответить
au 05.08.2009 10:28 #
Статья очень хорошая, но я только только начинаю работу с Zend - и мне не совсем понятно как работать с помощником placeholder, а именно где все это писать -  в своем помощнике чтоль? Мне например надо вывести форму авторизации в шапке сайта и чтобы она была видима постоянно + при авторизации вместо нее ( и на ее месте) было приветствие пользователю... Не врублюсь никак вообще что-то, уж простите такого тупого)))
Ответить
ion 11.10.2009 14:41 #
спасибо, очень полезно
Ответить
Дмитрий 22.08.2010 09:57 #
Я просто в ауте от этих статей, пытающихся разъяснить те или иные аспекты работы Zend Framework. Пока доберешься до сути вопроса, приходится фильтровать массу посторонней информации.
Вместо того, чтобы начать с главного - для чего нужны View Helpers и как они практически используются в ZF, авторы начинают с абстракций, рассуждений и тонкостей работы того, о чем читатель пока не имеет ни малейшего представления, попутно используя оригинальную терминологию, еще больше запутывающую смысл материала.
Разобраться в этом полуфантастическом бреде стоит большого труда.
Ответить
Александр Махомет 23.08.2010 15:00 #
Суть помощников вида описана во введении, вы не внимательно читали. Если вы не имеете представления о MVC концепции значит вы рано взялись за ZF и эту статью. Статья рассчитана на определенный уровень подготовки. Это не для тех кто только вчера познакомился с PHP.

P.S. Когда разберетесь обязательно напишите статью в которой вы изложите все так доступно как считаете нужным. Вот тогда и критикуйте.
Ответить
Алексей 01.12.2011 12:45 #
100% Согласен с Дмитрием.
Ответить
Сергей 11.12.2011 12:27 #
...полностью согласен, сюда в первую очередь заглядывают люди малознакомые с Zend Framework и соответственно статьи надо ориентировать на эту аудиторию, если конечно ставилось за цель дать полезной информации, а не просто поумничать... работаю с фреймворком более полугода, есть готовые проекты... но понятного в этой статье нашел мало...
Ответить
Дмитрий 22.08.2010 10:17 #
Одно только определение "Помощник вида это класс, который следует определенной конвенции наименования" чего стоит :) Абсолютно бессмысленный текст, не объясняющий ровно ничего. "Волны перекатывались через молл и падали вниз стремительным домкратом".
Ребята, если вы поставили перед собой цель тупо перевести тексты на русский - вы этой цели добились. Если же вы хотели сделать концепцию ZF более доступной и понятной для широких масс PHP программистов - у вас вышло с точностью до наоборот, запутали всё до максимальной степени.
Ответить
Александр Махомет 23.08.2010 15:02 #
Если для вас это бессмысленно, повторюсь вы рано взялись за чтение этой статьи.
И да, это перевод. Так что если очень хотите - то напишите изначальному автору и расскажите какая у него плохая статья. А тут такая ваша критика никому не нужна.
Ответить
Dmitry_f 28.08.2010 01:28 #
Нужна или нет - не знаю, но только что практически убил 30 минут впустую, пытаясь разобраться что к чему. Какие-то отрывочные сведения, так и не смог составить полной картины.
Ответить
Игорь 28.08.2010 01:41 #
Зато я вам могу смело теперь сэкономить гораздо больше времени - бросайте это гиблое дело, дворники и быдлокодеры тоже нужны, ничего страшного.

Так что, сайт вы в любом случае посетили не зря ;)
Ответить
wa1 06.11.2010 17:50 #
:D

Всегда удивляюсь, говнокодеры, переводите статьи сами и не жалуйтесь.
Ответить
Георгий 14.01.2011 10:05 #
Согласен с постами Дмитрия, те же самые мысли посетили при прочтении, сначала объясняется как они работают, а затем - для чего это надо и что они делают. Авторов статьи прошу не грубить в ответ, в любом случае за статью спасибо, но факт есть факт - новичку тут не разобраться, а не новичку это можно и не читать.
Ответить
Андрей 02.09.2011 09:52 #
Случайно наткнулся на статью и нашел ответ на мой вопрос. По поводу спора могу сказать, что для новичка конечно будет ничего не понятно, но когда какоето время потратил уже на изучение фреймворка то все становится понятно, лично мне эта инфа очень помогла:) Конечно может я плохо смотрел в английской инфе раз не заметил там эту информацию.
Ответить
Роман 25.09.2011 10:33 #
нормально расписано, очень полезная информация.

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

Спасибо за отличную статью и понятное изложение, даже если это просто банальный перевод с англ.
Ответить
Уважаемые пользователи. Комментарии не для того чтобы:
  1. Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
  2. Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.

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

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

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