Шаблоны

Введение
Заблуждения
Теория
Практика
Комментарии (38)

Введение
Отделение логики получения данных от логики их отображения - очень важная составляющая веб-девелопмента.
Любой программист, который поднялся чуть выше уровня "Hello world", начинает ощущать потребность в таком разделении. Но далеко не все приходят к правильным выводам и решениям.
Поэтому я приведу здесь самые важные правила:
1. Код получения и код отображения данных надо разделять.
2. Любой вывод должен начинаться только после того, как для него готовы все данные.
3. Как следствие, любой скрипт должен заниматься только обработкой данных. После этого он может либо отправить какой-то НТТР заголовок, или вызвать шаблон, передав ему подготовленные данные, или и то и другое вместе.
4. Каким именно шаблонизатором пользоваться - дело десятое. Самый простой и доступный - сам РНР, поэтому примеры будет приводиться на нём.

Заблуждения
Нет, наверное, в веб-программировании темы столь же очевидной, сколь и непонятной, как шаблоны. Каждый, рано или поздно, приходит к выводу о необходимости испрользования шаблонов. Но приходит, почему-то, через какие-то дичайшие заблуждения и фантазии.

Самое простое и очевидное заблуждение состоит в том, что новички называют шаблоном вынесенный в отдельный файл "дизайн" - общий html для всех страниц сайта. И на этом успокаиваются. Динамическую информацию, ничтоже сумняшеся, выводя старым добрым echo :-)
На самом же деле, шаблонизатор в основном занимается выводом изменяющегося содержимого страниц сайта. А вывод "дизайна" - задача второстепенная.

Фантазий же главных две:
1. Шаблоны нужны "дизайнеру", чтобы он мог их править, не разбираясь в PHP.
2. Следовательно, шаблоны служат для отделения PHP от HTML.

Давайте попробуем задуматься над первым утверждением. Кто такой дизайнер? Это человек, который работает в фотошопе. HTML он чаще всего не знает вообще. А над шаблоном работает либо специальный верстальщик или - чаще всего... сам программист! Смешно, правда?
Теперь следствие, про отделение PHP от HTML. Отлично. Перед нами стоит святая цель отделить. Поэтому мы придумываем Смарти и пишем:
{foreach key=cid item=con from=$contacts}
    <a href="contact.php?contact_id={$cid}">{$con.name} - {$con.nick}</a><br />
{/foreach}

Ещё смешнее.
"Дизайнер", ради которого все затевалось, падает в обморок от счастья.

Теория
Получается, что наши причины, по которым мы решили пользоваться шаблонами, гроша выеденного не стоят. И что же теперь - не нужны, выходит, шабоны вообще? Нужны. Но сначала надо ответить себе на вопрос - "зачем?" Для чего нужны шаблоны. И проверить ответ практикой. Я много раз задавал людям этот вопрос. Но почти никто не может на него ответить. Зачем ему нужны шаблоны. Получается, люди делают что-то, не зная зачем.
Это - самое смешное.

За время своей деятельности в качестве веб-программиста я сформулировал для себя три причины, по которым нужны шаблоны лично мне. По сути, их две. А сводятся, в конечном счете, к одной:

Один код - несколько представлений.

Часто бывает так, что вместо одной информации надо показать другую. Например, код работы с БД получает вместо текста новости сообщение об ошибке. В этом случае вместо страницы новости нужно показать совсем другую - с извинениями и просьбой зайти позже. С помощью шаблонов это делается элементарно.

Часто одну и ту же информацию надо показывать в нескольких видах. К примеру - обычная страница и страница для печати. Информация та же самая, код её получения один и тот же, а код вывода - разный. Столкнувшись с такой ситуацией, очень быстро разделишь свой код на две части, одна из которых отвечает за вывод, а вторая - не отвечает. Еще пример: допустим, нам захотелось выводить информацию не напрямую в HTML, а через AJAX-запрос, в формате JSON. Если у нас использовался шаблонизатор, то мы меняем в нашем коде ровно одну строчку - вызов шаблонизатора на вызов json_encode(). А если бы у нас вывод был перемешан с кодом получения данных, то код бы пришлось переписывать весь!

В чем-то похожая ситуация: допустим, наш скрипт стоит на двух сайтах. Плюс копия у нас дома. И вот дома мы нашли крупную багу. Заделали её. Теперь надо обновить код на сайтах. И вот он - момент истины: если шаблоны были использованы правильно, то мы просто заливаем код на оба сайта, и все продолжает работать, как ни в чем не бывало! Такая ситуация, как я считаю - идеальная проверка выбранного подхода к шаблонизации.

Ещё один важный момент, который многие упускают (в своих теоретических рассуждениях, при этом постоянно сталкиваясь с ним на практике!) - порядок выполнения скрипта не всегда совпадает с порядком вывода в шаблоне. Хрестоматийный пример - вывод заголовка статьи в теге <title>. Если мы выводим информацию по мере поступления, то просто не сможем этого сделать - шапка сайта уже выведена, к моменту, когда мы начали получать текст новости.

Следует также помнить, что кроме текста РНР скрипты еще и выводят HTTP заголовки. Которые обязаны выводиться до любого текста, или даже вместо текста вообще (если мы например хотим перенаправить пользователя на другую страницу). Если мы сначала реализуем логику работы приложения, ничего не выводя при этом, то выдать нужный НТТР заголовок не составит для нас никакой проблемы.

У вас могут быть и свои причины использовать шаблоны. Но только с одним условием - эти причины должны быть вызваны реальной, жизненной необходимостью, а не "высшими соображениями" и заботой о каких-то неизвестных вам людях.

Практика
Теперь от теории перейдем к практике.
В самом простом случае в показе любой страницы у нас всегда будут участвовать два шаблона: общий шаблон сайта и шаблон контента конкретной страницы.
Допустим, мы хотим сделать страницу со ссылками на сайты друзей.
В этом случае код упрощенный код будет выглядеть так:

сам файл links.php. НИЧЕГО не выводит. Только готовит данные и после этого вызывает шаблон.
<?
//инклюдим настройки.
include 'settings.php';

//получаем данные из базы, определяем переменные 
$pagetitle = "Ссылки";
$DATA = $db->getAll("SELECT * FROM links");

//задаем шаблон страницы и вызываем общий шаблон сайта
$tpl = "tpl_links.php";
include "tpl_main.php";


общий шаблон (tpl_main.php ):

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>My site. <?=$pagetitle?></title>
 </head>
 <body>
  <div id="page">
<?php include $tpl ?>
  </div>
 </body>
</html>


в нужном месте в него включается шaблон нашей страницы (tpl_links.php):


<h2><?=$pagetitle?></h2>
<ul>
<?php foreach($DATA as $row): ?>
  <li><a href="<?=$row['link']?>" target="_blank"><?= htmlspecialchars($row['name'])?></a></li>
<?php endforeach ?>
<ul>