Pull to refresh

Контекстные шаблоны в Native PHP

Reading time5 min
Views1.9K
Священая война шаблонов продолжается по сей день. На одной стороне выступают «угловатые», называемые так за то, что на погонах у них значится <? и ?>. Вообще говоря тут надо использовать единственное число, поскольку «угловатый» он один большой и неуклюжий медведь. А противостоит ему целая армия более мелких «кучерявых». Они все в разной форме, но чаще всего на погонах можно увидеть знаки { и }. Война идет затяжная с переменным успехом обеих сторон. Где-то в стороне стоит и наблюдает «независимый» XSLT, но его мы упомянули только для проформы, поскольку речь пойдет не о нем, а о любви.

Метаться как обезьяна между умными и красивыми нет никакого смысла, поэтому надо полюбить одну какую-то сторону. Любовь будет по расчету, как вы понимаете. Автор сделал свой выбор, война для него закончилась, и убеждать его в преимуществах другой стороны уже нет смысла. Он прекрасно понимает все плюсы и минусы каждой. (Если вы яростный противник Native PHP, то с целью сохранения своего психического равновесия, предлагаем отказаться от дальнейшего чтения даной статьи.) С любовью оно как получается? Выбираешь себе объект любви и потом уже лепишь из него прекрасного принца или на худой конец белого коня. Проницательный читатель уже догадался, что автор подался в стан «угловатых» и будет рассказывать как из медведя лепить… контекстные шаблоны.

В чем основной недостаток Native PHP? В том, что он слишком низкоуровневый и поэтому «многословный». С этим пороком любимого и будем бороться. Один из первых способов, который приходит на ум – функции-хелперы. То есть некие «трудные» куски шаблонов просто помещaются в тело вспомогательных функций. Например вам надо произвести выдачу денежных величин в таблице, причем отрицательные показать красным. Как это бы выглядело в чистом Native PHP:

<table>
 …
 <? if ($sum<0) $color='red'; else $color='black'; ?>
 <tr><td style=«color:<?=$color?>»><?=$sum?></td></tr>
 …
</table>

Ну как-то не элегантненько получается. Если ввести некую функцию-хелпер, то будет смотреться лучше:

<table>
 …
 <tr><?=ShowSum($sum)?></tr>
 ... 
</table>


Лучше-то оно лучше, но тоже есть «но». Нарушается семантическая целостность шаблона. Часть его относится куда-то в конец файла или в другой файл. «Того, кто стоит в стороне и наблюдает», такой подход возможно не удивит. Но поэтому он и стоит в стороне мало кому нужный, а нам же надо добиться «красивого» и полноценного шаблона.

Есть еще одна опасность в таком подходе, называямая «шаблон в кавычках». Рано или поздно, работая с хелперами придется передавать какие-то части шаблонов, как литералы языка. Это очень малоконструктивный подход, подрывающий всю идею шаблонов.

А теперь ближе к телу. Парсить все-таки придется! Но парсить будем не все, а только микро куски и только на предмет того, что нам требуется в данном контексте. Поэтому на общую скорость работы приложения это отразится очень мало. У нас будет все тот же Native PHP.

Для того чтобы продемонстрировать элегантность решения, возьмем гораздо более сложный случай – форму. Сама тема создания форм заслуживает отдельного рассмотрения, но здесь мы ее пустим второй сюжетной линией, без детального углубления, лишь закрепив здесь на видном месте лозунг: «Нет формопостроителям!» Как выглядел бы шаблон формы в обычном Native PHP:


<label for=«field_name»>Name:</label>
<input type=«text» id=«field_name» name=«name» value="<?=$name?>" style=«width:100px»>

<label for=«field_country»>Country:</label>
<select id=«field_country» name=«country» style=«width:100px»>
 <? foreach ($countries as $c=>$countryname): ?>
  <? if ($c==$country) $selected='selected'; else $selected=''; ?>
  <option value="<?=$c?>" <?=$selected?>><?=$ countryname?></option>
 <? endforeach ?>
</select>


Тихий ужас, правда? Самое интересное, что в «кучерявых» шаблонах общего назначения данный конкретный пример будет выглядеть ни чем не лучше. А нашими контекстными шаблонами мы убьем двух зайцев сразу: во-первых спрячем низкий уровень, а во-вторых покажем, как можно красиво и оптимально делать формы без формопостроителей. Таким образом окажемся впереди планеты всей!

Дело в том, что множество атрибутов, такие как “id”, “value”, “name” не имеют к дизайну шаблона прямого отношения. Следовательно их можно спрятать. Вот как будет выглядеть тот же кусок по-новому:


<? form::element('name', $name) ?>
<label>Name:</label> <input type=«text» style=«width:100px»>

<? form::element('country', $country, $countries) ?>
<label>Country:</label> <select style=«width:100px»></select>



Ну вот совсем другое дело! Очень понятный, семантически правильный шаблон. Ничего лишнего и в то же время все необходимое в одном месте. Мы спрятали ненужные дизайнеру аттрибуты (name, value и т.п.), тогда как всеми другими он волен оперировать свободно. У нас в примере стоит только style, но можно добавлять все, что угодно без ограничений. Нет никаких проблем и в дополнительных тегах. Так <label> и <input> могли бы оказаться в разных div'ах или ячейках таблицы, это допускается:

<? form::element('name', $name) ?>
<tr>
 <td align=right><label>Name:</label></td>
 <td align=left><input type=«text» style=«width:100px»></td>
</tr>


(Не обращайте внимания на плохой стиль HTML, это лишь демонстрация возможностей)

Для select'а можно задать начальное значение. Или не задавать начальное, но задать визуальные свойства option'ов (или и то, и другое одновременно):

<? form:: element('country', $country, $countries) ?>
<label>Country:</label>
<select style=«width:100px»>
 <option value="">--Choose please--</option>
</select>

<? form:: element('country', $country, $countries) ?>
<label>Country:</label>
<select style=«width:100px»>
 <option style=«background-color:yellow»></option>
</select>


Что мы здесь имеем? На самом деле ничего нового. <? form::element(...) ?> в данном случае выступает аналогом так любимого «кучерявыми» {block …} (с неявным закрытием блока в начале следующего). Содержимое блока буферизируется, парсится и расширяется всеми необходимыми техническими деталями. Таким образом мы избавились от «многословности» Native PHP, повысыв его уровень и практически не потеряв в скорости.

Посмотрите еще один пример, тот же кусок, но дополнительно с кодом подсвечивания ошибки. Те поля, что нужно, обводятся красной рамкой с не менее красным текстом ошибки и забавной стрелочкой:

<? form::error($element,$msg) ?>
 <span style=«color:red;border:solid 1px red;»>
  <?=$element?> ← <?=$msg?>
 </span>
<? form::error() ?>


<? form::element('name', $name) ?>
<label>Name:</label> <input type=«text» style=«width:100px»>

<? form::element('country', $country, $countries) ?>
<label>Country:</label> <select style=«width:100px»></select>



Обратите внимание, что сам код формы остался девственно чистым. Лишь добавился еще один блок вначале.

Вот пожалуй и все! Нашей задачей было лишь изложить методу, не предлагая конкретного решения. Сама идея «контекстности» подразумевает создание конкретного решения для конкретной задачи. Иначе мы попадем под угрозу очередного скатывания в «кучерявость». Впрочем использованный здесь пример с формой достаточно общий и может найти применение в любой задаче. Решение такое существует и желающие могут познакомиться с ним на phella.net/?page=approach#forms Собственно название блока «form::element» оттуда и взято.

Примечание: автор знаком с альтернативной семантикой HTML тега <label>, избавляющего от атрибутов “for” и “id”. Но это здесь не важно. Когда мы избавились от них вместе со множеством других, в таком виде <label> смотрится более компактно.

По поводу типографских кавычек в HTML коде вопросы к хабру
Tags:
Hubs:
Total votes 24: ↑12 and ↓120
Comments32

Articles