Код в шаблонах: две стороны одной медали

Страницы интернета в темах про разработку сайтов пестрят громкими фразами: скажем нет говнокоду! Нет коду в шаблонах! Разделим систему на логику и представление! Никакого выполняемого php и запросов к БД в html-разметке страницы!

Однако многие серьёзные фреймворки, до которых некоторым разработчикам в своём умственном развитии далеко как до луны раком, штатно применяют программный код прямо в шаблонах. Это и Zend Framework, и Symphony, и Ruby on Rails, и даже новомодный Yii. Казалось бы, куда катится мир? Неужели всё делается в угоду недоразвитым “программистам”, которые прочитали “самоучитель PHP за 30 минут” и хотят делать сайты без изучения MVC, REST, XSLT и прочих “взрослых” технологий?

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

Когда вы пишете прямо в html-коде, например,  строку “<? echo $page->content() ?>” – задумайтесь, что такое “$page” на самом деле. Это – модель. Та самая модель в паттерне Model-View-Controller (MVC), за который вы боролись в предыдущем абзаце. Но это не импотентская модель, у которой можно только брать свойства, как сделано в некоторых CMS. Это полноценная модель, способная жить своей жизнью:

Во-первых, фильтры на вывод свойств. Та самая “стрелочка” – это вызов метода модели, который не только выводит содержимое, но может и фильтровать его. Например, на вывод $page->title можно повесить фильтр, исключающий html-тэги, ибо нефиг в title страницы выводить что-то кроме текста. На вывод $page->email можно повесить декоратор, который автоматически будет подставлять <a href=”mailto:…”> или шифровать емайл от спаммерских роботов. Я не говорю уж о том, что время создания объектов (новостей, статей и т.д.) через такой же декоратор будет выводиться в национальном формате независимо для каждого пользователя – день/месяц/год для нас или mm-dd-yyyy для англоязычных.

Во-вторых, декораторы для inPlace-редактирования. Например, для администратора сайта можно оборачивать вывод значений в специальную html-разметку, предоставляющую редактирование “на месте” (in place). А для обычного посетителя никаких специальных тэгов нет – и поисковым роботам тоже отдаётся полностью валидный html-код, без лишних тэгов. W3C Compatible и SEO-friendly в полный рост.

В-третьих, валидация полей при создании или редактировании объектов. Если речь идёт о заголовке поста блога $post->title(), то ему можно приписать условие валидации: “unique”. Вы же не хотите, чтобы у вас в блоге было два поста с одинаковым заголовком? Вот и система не даст вам создать new $post, если такой title уже существует.

В-четвёртых, генерация форм. Пройдите циклом по всем полям модели, наложите на них соответствующие input, textarea, select или radiobutton. Вот вам готовая форма, которая генерируется динамически. Более того, если вы наложите $_POST на эту форму, то при ошибочно заполненных полях получите красную подсветку и сообщения об ошибках индивидуально для каждого поля (а не где-то над формой в общей куче).

В-пятых, если вы сделаете то же самое в админке, то получите динамический интерфейс администрирования (scaffolding). Причём сразу с живыми подсказками – что можно и что нельзя вводить в каждое поле индивидуально.

В-шестых, если вы свяжете одну модель с другой, то вы сможете оперировать ими совместно: $author->articles() даст вам список статей автора, $article->author() даст вам автора статьи. Запись вида $country-> $manufacturers-> $products даст вам список товаров, которые поставляются из указанной страны.

В-седьмых, у вас есть удивительная лёгкость выполнения действий: если у вас $cart и $order строятся по одной модели, то новый заказ в интернет-магазине на вашем сайте делается как new $order = $cart. Сравните с объёмом кода для аналогичного действия в CMS, которую вы сейчас используете.

В-восьмых, задумайтесь сколько SQL-запросов отсылается в БД при выводе $page->content. Всего один! Здравствуйте, высоконагруженные проекты. И никаких затрат времени на трансформацию шаблонов.

В-девятых, эвенты (events, события). На любое изменение любого поля модели можно повесить любой эвент, и отслеживать любые взаимосвязанные события.

В-десятых, полноразмерная поддержка вашего любимого REST. Очевидно, что GET /articles/20/ читает двадцатую статью, а POST на тот же адрес – записывает её.

Но что я бы это поставил на первое место – это лёгкость обучения: низкий порог входа для новых разработчиков. Если вы в силах обучить кого-то своему шаблонизатору и своему API быстрее, чем он научится писать $page->content(), то я снимаю перед вами шляпу. Но это фантастика.

И это всего лишь небольшая часть фреймворка, который строится вокруг скромного $page. Поэтому когда вокруг меня говорят “говнокод в шаблонах – фууу”, я скромно молчу. Потому что я знаю, в чём тут дело. Потому что я знаю, что бизнес-логика должна жить в системе, но я также знаю, что логика вывода – должна жить в шаблонах. И не мешайте ей это делать.

P.S. Всеми этими принципами я руководствовался, когда создавал первую версию UMI.Framework, чтобы наконец уйти от избыточной сложности XSLT-шаблонизатора Юми и дать в руки разработчикам нормальный и привычный стиль написания кода. В настоящее время Юми-фреймворк это открытый проект, к развитию которого может присоединиться любой желающий.