По следам хабратопика Наследование шаблонов в Smarty хочу рассказать как около полутора лет назад я решил подобную задачу. В оличие от marazmiki с Django я знаком не был (хотя сейчас занимаюсь изучением именно его). Но крайне поверхностно был знаком с механизмом Master Pages из ASP.NET. Механизм заключается в том, что у нас есть практически полноценная страница, у которой отсутствует только блок с основным контентом.

Немного прикинув придумалось решение через стандарные функции Smarty и механизм наследования в PHP.


Прежде всего шаблон смарти:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" «www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd»>
<html xmlns=«www.w3.org/1999/xhtml»>

<head>
   <title>Main title{if i$ADDITIONAL_TITLE} | {$ADDITIONAL_TITLE}{/if}</title>
   <base href="{$ROOT_URL}" />
   <meta http-equiv=«Content-Type» content=«text/html; charset=UTF-8» />
</head>

<body>

<div id=«base_div»>
   <div id=«header_logo_div»>{include file=«header_logo.tpl»}</div>
   <div>
      <div id=«user_login_info»>{include file=«users/user_login_info.tpl»}</div>
      <div id=«lang_selector»>{include file=«lang/selector.tpl»}</div>
   </div>
   <div>
      <div id=«main_block»>
         {include file=$__file_to_display}
      </div>
   </div>
</div>

* This source code was highlighted with Source Code Highlighter.


Основные элементы на которые стоит обратить внимание это:
1. Main title{if $ADDITIONAL_TITLE} | {$ADDITIONAL_TITLE}{/if}
2. {include file=$__file_to_display}

Далее идет код, который со всем этим работает. Прежде всего, для применения настроек смарти и работы с данными общими для всего сайта у меня был создан абстракный класс, который наследовался напряму от Smarty:

<?php
abstract 
class smarty_page_ex extends Smarty {
    
protected $module;
    
protected $action;

    
public function __construct($module = "", $action = "", smarty_config_wrapper $config = null) {
        
parent::Smarty();

        
$this->module = $module;
        
$this->action = $action;

        
$gconfig = global_config::get_instance();

        if (
$config === null) $config = $gconfig->get_smarty_config();

        
$this->compile_check = $config->get_tpl_compile_check();
        
$this->debugging = $config->get_smarty_debug();

        
$this->compile_dir = $config->get_tpl_compiled_path();
        
$this->cache_dir = $config->get_tpl_cache_path();

        
$this->template_dir = $config->get_tpl_path();
        
$this->config_dir = $config->get_tpl_config_path();

        
$lang = lang::get_instance();
        
$this->assign_by_ref("lang", $lang);

        
$this->assign("ROOT_URL", $gconfig->root_url);
        
$this->assign("MODULE", $module);

        
$this->assign("ACTION", $action);

        
$this->register_object("smarty_page_ex", $this);
    }

    
/**
     * Gets template full path
     *
     * @param string $base_path
     * @return string
     */
    
protected function get_full_path() {
        return (
strlen($this->module) > 0 ? ($this->module . "/") : "");
    }
}
?>



Тут стоит упомянуть что структура хранения шаблонов в проекте имела следующий вид:
<templates-dir>/
|-<module>
|  |-<action>.tpl
|-home
|  |-index.tpl
|-common_page.tpl
|-admin_page.tpl
|-mail_page.tpl


А шаблон приведенный выше является common_page.tpl (упрощенный меташаблона для пользовательских страниц).

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

<?php
class common_page extends smarty_page_ex {
    
public function __construct($module = "", $action = "", $title = "", smarty_config_wrapper $config = null) {
        
parent::__construct($module, $action, $config);

        
$options = options::get_instance();
        
$this->assign_by_ref("options", $options);

        
$auth_user = auth_user::get_instance();
        
$this->assign_by_ref("auth_user", $auth_user);

        
$gconfig = global_config::get_instance();
        
$this->assign_by_ref("gconfig", $gconfig);

        
$lang = new languages();
        
$lang_list = db_adapter::get_instance()->get_list($lang);
        
$this->assign_by_ref("__languages", $lang_list);

        
$this->assign("ADDITIONAL_TITLE", $title);
    }

    
/**
     * Executes & returns or displays the template results
     *
     * @param string $template
     * @param string $cache_id
     * @param string $compile_id
     * @param boolean $display
     */
    
public function fetch($template, $cache_id = null, $compile_id = null, $display = false) {
        
$this->assign("__file_to_display", $this->get_full_path() . $template);
        return 
parent::fetch("common_page.tpl", $cache_id, $compile_id, $display);
    }
}
?>


Тут в шаблон отдаются какие-то общие данные и дополнительный заголовок, если требуется, все как обычно. А далее мы переопределяем метод fetch базового класса Smarty так, чтобы он не компилировал отданный ему шаблон, а вставлял его в меташлаблон, и отдаем на компиляцию уже меташаблон со вставленным блоком данных.

В простейшем виде использование такой структуры кода сводится к следующему:

<?php
$tpl 
= new common_page("home", "index", "Welcome!");
$tpl->display();
?>



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

Готов выслушать критику в свой адрес по поводу данного спооба наследования шаблонов в смарти.