Композитные макеты

Содержание...

Существует распространённая задача генерации набора сложных страниц, объединенных общим дизайном и содержащих по несколько динамических блоков, некоторые из которых являются одинаковыми для всей группы или только для части страниц. Например, когда на всех страницах вне зависимости от их основного содержания нужно отображать меню текущего раздела, которое генерируется динамически, и список последних новостей.

Для упрощения решения подобных задач во фреймворке реализована поддержка макетов (layout).

Общая концепция

Макет — это шаблон общего обрамления, содержащий внутри себя области для включения HTML-кода, генерируемого различными экшенами. Одна из этих областей считается основной и заполняется кодом, полученным в результате выполнения экшена или контроллера, получившего управление для обработки поступившего запроса. Иными словами, основная область — это область отображения главного смыслового контента страницы. Экшены для заполнения остальных областей вызываются непосредственно из самого макета.

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

Каждый макет состоит из двух файлов:

Простой пример использования

Классическая схема: общий макет страницы, в который включены два динамических блока:

Файлы шаблонов макетов должны располагаться в подкаталоге приложения templates/layouts/. Имя файла шаблона должно совпадать с именем макета.

Назовем макет именем Default и создадим файл шаблона для этого макета templates/layouts/Default.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>{$wa->appName()} &mdash; {$wa->accountName()}</title>
  {$wa->css()}
  <script type="text/javascript"
  src="{$wa_url}wa-content/js/jquery/jquery-1.5.2.min.js"></script>
</head>
<body>
  <div id="wa">
  {$wa->header()}
    <div id="wa-app">
      <div class="sidebar left200px">
        {$sidebar}
      </div>
      <div class="content left200px">
        {$content}
      </div>
    </div>
  </div>
</body>
</html>

Переменная $content в шаблоне является системной — при рендеринге страницы она будет заменена на результат выполнения основного экшена.

Название класса макета должно строиться в горбатом стиле по следующей схеме: {APP_ID}{Название Макета}Layout. PHP-файл класса макета должен располагаться внутри каталога приложения lib/ (рекомендуется выделить для классов макетов отдельный подкаталог lib/layouts/) и быть именован по правилу {название класса без суффикса Layout}.layout.php.

Создаем класс макета dummyDefault.layout.php. Класс макета должен наследоваться от системного класса waLayout:

<?php

class dummyDefaultLayout extends waLayout
{

    // Здесь определяются блоки, доступные в шаблоне
    public function execute()
    {
        // Результат выполнения экшена dummySidebarAction будет
        // доступен в шаблоне макета в переменной $sidebar
        $this->executeAction('sidebar', new dummySidebarAction());
    }

}

Теперь созданный макет можно привязать к экшенам и контроллерам. Привязка макета доступна для классов waViewAction, waViewActions и waViewController. Привязка макета производится следующей строкой кода:

$this->setLayout(new dummyDefaultLayout());

После такой привязки результат экшена будет вставлен в шаблон макета вместо переменной $content, а остальные переменные шаблона макета будут заменены результатами выполнения экшенов указанных в классе макета dummyDefaultLayout.

Варианты использования макетов

Ниже представлено несколько советов и хитростей по организации и использованию макетов.

Единый макет на все приложение

Все страницы приложения однотипные и используют один и тот же макет, меняется только содержимое основного блока.

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

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

В приложении всегда можно переопределить контроллер по умолчанию. Для этого нужно создать конфигурационный файл wa-apps/{APP_ID}/lib/config/factories.php со следующим содержимым:

<?php

return array(
    'default_controller' => 'dummyViewController'
);

Здесь dummyViewController — это имя класса-контроллера по умолчанию.

Если конфигурационный файл factories.php уже существует, то в него достаточно добавить соответствующую строку.

Класс контроллера в этом случае наследуется от waDefaultViewController и должен выглядеть следующим образом:

<?php

class dummyViewController extends waDefaultViewController
{

    public function execute()
    {
        $this->setLayout(new dummyDefaultLayout());
        parent::execute();
    }

}

Макет для группы страниц с простой логикой

Есть несколько групп небольших и простых страниц, и для каждой группы нужен свой макет. Страницы простые, поэтому экшены построены на основе класса waViewActions, в котором разные экшены представлены в виде отдельных методов одного класса.

  1. Создаем необходимое количество макетов.
  2. Для каждой группы страниц создаем свой класс экшенов унаследованный от waViewActions, где в методе preExecute задаем нужный макет, единый для всей группы страниц:
<?php

class dummyTestActions extends waViewActions
{

    // Этот метод вызывается до выполнения любого экшена
    public function preExecute()
    {
        $this->setLayout(new dummyDefaultLayout());
    }
  
    public function defaultAction()
    {
        ...
    }
  
    // шаблон templates/actions/test/TestPage1.html
    public function page1Action()
    {
        ...
    }
  
    // шаблон templates/actions/test/TestPage2.html
    public function page2Action()
    {
        ...
    }

}

Отдельный макет для одной страницы

Если макет нужен лишь для некоторых отдельных страниц приложения, то достаточно создать для каждой нужной страницы свой контроллер, унаследованный от waViewController:

<?php


class dummyTestPageController extends waViewController
{

    public function execute()
    {
        $this->setLayout(new dummyDefaultLayout());
        $this->executeAction(new dummyTestPageAction());
    }

}