Наследование шаблонов в Smarty

Когда-то, давным-давно, мне пришлось использовать небезызвестный шаблонизатор Smarty. Сначала я, понятное дело, возмущался и кричал, какая же гадость эта заливная рыба 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>

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>
Идея вкратце такова: внутри объекта шаблонизатора введём ассоциативный массив, ключами которого будут имена наследуемых блоков, а соответствующими им значениями — массивы, содержащие текстовые содержания этих блоков, хранящиеся в порядке их (блоков) вызова. Согласен, фраза получилась заумной, поэтому проще показать на предыдущем примере:
 
<code>Array

(

    [foo] => Array

        (

            [0] => It`s a parent

            [1] => It`s a child

        )

)</code>
Надеюсь, всё просто. Теперь остаётся при вызове блока в шаблоне «достать» из этого хранилища последний элемент и отобразить его на месте тегов :)

Как я уже писал выше, для реализации нам понадобится зарегистрировать 2 блока с именами extends и block, а так же ввести хранилище значений.

Пусть блок {extends}{/extends} будет отвечать за получение исходного кода шаблона-родителя, а {block}{/block} — за создание и переопределение наследуемых блоков.

Мануал поможет нам создать блоковые плагины:
block.extends.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`]);

}



?>
block.block.php:
<?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);

}
Здесь надо сказать, что setBlock() и getBlock() — методы шаблонизатора, которые соответственно помещают и получают текстовые значения наследуемых блоков из стека, про который было сказано выше. Расширим класс Smarty, введя массив стека и методы:

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 в комплект поставки, естественно, не входит).

Спасибо за внимание :)
 

Источник: habrahabr



Опубликовал admin
30 Авг, Суббота 2008г.



Программирование для чайников.