Когда-то, давным-давно, мне пришлось использовать небезызвестный шаблонизатор Smarty. Сначала я, понятное дело, возмущался и кричал, какая же гадость эта заливная рыба Smarty, а потом «распробовал» и втянулся. Те удобства, которые он давал, с лихвой компенсировали мысли о том, что есть и более быстрые шаблонные движки.
Шаблоны я обычно строил с помощью инклюдов: в начале подключался header.tpl, в конце — footer.tpl, в середине ещё что-нибудь нужное. В целом разметка получалась довольно аккуратной, но не проходило ощущение, что не хватает чего-то важного. Окончательно понимание этого чего-то появилось, когда мне случилось написать простенькое приложение на Django. И это «что-то», как все поняли, оказалось наследованием шаблонов. Простая, как и всё гениальное, идея позволяла существенно упростить шаблоны и избавиться от дублирующих блоков.
Решение оказалось не сложнее самой идеи наследования, которая, напомню, была простой, как и всё гениальное :)
parent.tpl:
child.tpl:
index.php:
А результат работы выглядит вот так:
Как я уже писал выше, для реализации нам понадобится зарегистрировать 2 блока с именами
Пусть блок
Мануал поможет нам создать блоковые плагины:
block.extends.php:
mySmarty.class.php
Теперь, подключив
Ленивые могут скачать готовый пример шаблонов и пощупать на деле (архив весит 2.2 кб, Smarty в комплект поставки, естественно, не входит).
Спасибо за внимание :)
Шаблоны я обычно строил с помощью инклюдов: в начале подключался header.tpl, в конце — footer.tpl, в середине ещё что-нибудь нужное. В целом разметка получалась довольно аккуратной, но не проходило ощущение, что не хватает чего-то важного. Окончательно понимание этого чего-то появилось, когда мне случилось написать простенькое приложение на Django. И это «что-то», как все поняли, оказалось наследованием шаблонов. Простая, как и всё гениальное, идея позволяла существенно упростить шаблоны и избавиться от дублирующих блоков.
Решение оказалось не сложнее самой идеи наследования, которая, напомню, была простой, как и всё гениальное :)
Примечание: дабы не плодить сущего, я не буду пересказывать статью про наследование шаблонов в Django, однако рекомендую её прочитать, дабы примерно понять, что нас ждёт и чтобы по исходным текстам шаблонов можно было понять, что они делаютВопреки расхожему мнению, одной из главных задач Smarty является не банальная замена
<?php echo $var ?> более лаконичными {$var}, а расширение базовой функциональности плагинами. В частности, Smarty позволяет определять собственные блоковые функции. Именно этим и воспользуемся.Примечание: в отличие от Django, здесь будет использован не одиночный тегСинтаксис шаблонов наследования будет примерно таким:{% extend %}, а блок{extends}...{/extends}, в пределах которого будут располагаться наследуемые блоки. Сделано это было, во-первых, из-за простоты реализации, во-вторых — этот подход даёт возможность наследовать разные шаблоны (хорошо это плохо — вопрос другой; в крайнем случае, никто не заставляет использовать несколько блоков{extends}в одном шаблоне).
parent.tpl:
<html> <head> <title> Inherit it! </title> </head> <body> <p>Just a paragraph</p> <p>{block name="foo"}It's a parent{/block}</p> </body> </html></pre>
child.tpl:
{extends template="parent.tpl"} {block name="foo"}It's a child{/block} {/extends}
index.php:
Особо, думаю, ничего пояснять не надо: перед компиляцией шаблона блок<?php $smarty->display('child.tpl'); ?>
{extends} заменяется содержимым шаблона, который указан в параметре template блока. Все именованные блоки, которые были определены внутри {extends}, перекрывают соответствующие блоки в родительском шаблоне.А результат работы выглядит вот так:
Идея вкратце такова: внутри объекта шаблонизатора введём ассоциативный массив, ключами которого будут имена наследуемых блоков, а соответствующими им значениями — массивы, содержащие текстовые содержания этих блоков, хранящиеся в порядке их (блоков) вызова. Согласен, фраза получилась заумной, поэтому проще показать на предыдущем примере:<html> <head> <title> Inherit it! </title> </head> <body> <p>Just a paragraph</p> <p>It's a child</p> </body> </html>
Надеюсь, всё просто. Теперь остаётся при вызове блока в шаблоне «достать» из этого хранилища последний элемент и отобразить его на месте тегов :)Array ( [foo] => Array ( [0] => It's a parent [1] => It's a child ) )
Как я уже писал выше, для реализации нам понадобится зарегистрировать 2 блока с именами
extends и block, а так же ввести хранилище значений. Пусть блок
{extends}{/extends} будет отвечать за получение исходного кода шаблона-родителя, а {block}{/block} — за создание и переопределение наследуемых блоков. Мануал поможет нам создать блоковые плагины:
block.extends.php:
block.block.php:<?php /** * Блок, наследующий шаблон * * @param array $params Список параметров, указанных в вызове блока * @param string $content Текст между тегами {extends}..{/extends} * @param mySmarty $smarty Ссылка на объект Smarty */ function smarty_block_extends($params, $content, mySmarty $smarty) { /** Никому не доверяйте. Даже себе! */ if (false === array_key_exists('template', $params)) { $smarty->trigger_error('Укажите шаблон, от которого наследуетесь!'); } return $smarty->fetch($params['template']); } ?>
Здесь надо сказать, что setBlock() и getBlock() — методы шаблонизатора, которые соответственно помещают и получают текстовые значения наследуемых блоков из стека, про который было сказано выше. Расширим класс Smarty, введя массив стека и методы:<?php /** * Создаёт именованные блоки в тексте шаблона * * @param array $params Список параметров, указанных в вызове блока * @param string $content Текст между тегами {extends}..{/extends} * @param mySmarty $smarty Ссылка на объект Smarty */ function smarty_block_block($params, $content, mySmarty $smarty) { if (array_key_exists('name', $params) === false) { $smarty->trigger_error('Не указано имя блока'); } $name = $params['name']; if ($content) { $smarty->setBlock($name, $content); } return $smarty->getBlock($name); }
mySmarty.class.php
<?php class mySmarty extends Smarty { /** * Список зарегистрированных блоков в шаблонизаторе * * @var array */ protected $_blocks = array(); /** * Конструктор класса * * @param void * @return void */ public function __construct() { $this->Smarty(); } /** * Регистрирует наследуе��ый блок шаблона * * @param string $key * @param string $value * @return void */ public function setBlock($key, $value) { if (array_key_exists($key, $this->_blocks) === false) { $this->_blocks[$key] = array(); } if (in_array($value, $this->_blocks[$key]) === false) { array_push($this->_blocks[$key], $value); } } /** * Возвращает код блока согласно иерархии наследования * * @param string $key * @return string */ public function getBlock($key) { if (array_key_exists($key, $this->_blocks)) { return $this->_blocks[$key][count($this->_blocks[$key])-1]; } return ''; } } ?>
Теперь, подключив
mySmarty.class.php, можно создавать объект класса mySmarty и пользоваться прелестями наследования шаблонов. Ленивые могут скачать готовый пример шаблонов и пощупать на деле (архив весит 2.2 кб, Smarty в комплект поставки, естественно, не входит).
Спасибо за внимание :)