Шаблоны: правила игры

Фанат

oncle terrible
Команда форума
Очень хочется догрызть тему шаблонизации.
Соответственно, хочется установить два основных правила, в соответствии с которыми проводить все дальнейшие рассуждения, сравнения и ругань.

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

2. Реалистичность.
Это самое сложное и вообще никем никогда не применяемое правило.
Я и сам не избежал этого подвоха в своих статьях и рассуждениях:
Как пример шаблона всегда рассматривается простая страница примитивного хомяка.
При том что владельцев хомяков проблемы шаблонизации вообще не волнуют.
А реальная проблема, которую призваны решать шаблонизаторы - это сборка шаблона сложного сайта

В понимании принципа шаблонизации каждый девелопер проходит три(?) основные стадии:
1. Шаблон - это шапка сайта. контент отдельных страниц ничтоже сумняшеся выводится прямо из скрипта.
2. Шаблонов должно быть два вида - основной для группы страниц (с шапкой и подвалом) и отдельные для каждого типа страниц.
3. Но тут вдруг оказывается, основной шаблон занимается не только (а часто - даже не столько) включением шаблона конкретной страницы в "дизайн сайта", сколько включением ещё десятка(!) модулей, каждый со своим отдельным выводом. Разрушается такая стройная теория линейного выполнения - запустился модуль, получил данные, определил файл шаблона, запустил шаблонизатор. Ну да, для основного блока данные получены, а для остальных их где брать? Скажем, облако тегов - когда и где для него данные получать?

Получается, нужен какой-то супервизор, который будет знать, какие блоки данных нам надо подгрузить ДО момента, когда мы будем начинать шаблонизацию.
Насколько я понимаю, этот супервизор и является основной проблемой и местом ломания копий. Собственно, противоречие сие описал Котеров ещё в прошлом веке: активный шаблон vs. пассивный.
Активный сам занимается поиском модулей, которые должны дать ему данные, а пассивный занимается только выводом, а поиском модулей занимается кто-то другой.

И в этом контексте давайте в качестве примера рассматривать шаблон гипотетической страницы контактов сайта. На который выводится одна-единственная переменная - тупой хтмл прямиком из TinyMCE.
Этим мы закрываем вопрос шаблонизации отдельной страницы.
Но при этом на странице ещё выводится форма авторизации/привет-ссылка на кабинет, облако тегов, сайдбар с лентой новостей и баннерка. На странице новостей вместо ленты выводится опорос.
И давайте теперь будем пробовать со всей этой хернёй взлететь.

ещё важные правила:

3. Одна из целей шаблонизации - не начинать вывод, если нет всех необходимых данных.
4. Еще одна цель - по максимуму уменьшить количество логики в шаблоне.
 

С.

Продвинутый новичок
Я не понимаю страха перед активными шаблонами. Никаких концептуальных нарушений и все работает без всяких супервизоров. Пассивный шаблон не имеет никаких преимуществ, только недостатки.
3. Одна из целей шаблонизации - не начинать вывод, если нет всех необходимых данных.
Вывод отдельного блока или всей страницы? И почему это важно, не выводить без данных? Что случится если шаблон выйдет пустым?
4. Еще одна цель - по максимуму уменьшить количество логики в шаблоне.
В данном контекте активный vs. пассивный это не играет роли. Ни один из этих методов по своей природе не увеличивеат количество логики в шаблоне.
 
  • Like
Реакции: NeD

Фанат

oncle terrible
Команда форума
Вывод отдельного блока или всей страницы?И почему это важно, не выводить без данных?
Всей страницы.
Тому две основные причины
1. Возможные ошибки. Если мы уже начали вывод и вылезла ошибка - вывод получится рваным.
2. Данные тупо могут влиять на вывод. Скажем, ну, за уши притянем пример - в баннере используется имя авторизованного пользователя. Если мы уже вывели баннер - мы туда имя не впихнём.
Но вообще, наверное, всё это решается, если ограничиться правилом для блока, а не для страницы.

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

Ни один из этих методов по своей природе не увеличивеат количество логики в шаблоне.
Не согласен.
Все эти parent, inherit, extends - 'это логика.
 

fixxxer

К.О.
Партнер клуба
Еще одна цель - по максимуму уменьшить количество логики в шаблоне.
А что плохого в том, что логика, связанная исключительно с генерацией html-кода, находится в шаблоне?

Получается, нужен какой-то супервизор
А тут мы подходим к вопросу про MVC. Я уже не раз говорил, что если внимательно посмотреть на классическое описание паттерна (как в десктопных приложениях), то можно заметить, что "C" в современных веб-фреймворках - это Front Controller. А то, что "в вебе" называют контроллером страницы, это скорее View Controller (в десктопных приложениях тоже разделяется код непосредственно отображения, и как бы "пассивный шаблон", в качестве которого выступают, например, "ресурсы", и никого эта "немонолитность" представления не смущает). В Django, кстати, такой терминологии и придерживаются.

Так что все эти рассуждения это на мой взгляд перестановка мебели - что мы сунем в контроллер отображения, а что в шаблон. Тут влияют две вещи нетехнического толка:
1) вкусовые предпочтения - на вкус и цвет карандаши разные
2) организационные причины - бывают, например, такие требования при разделении труда, когда верстальщику надо минимально работать с логикой, в этом случае в view controller переносятся те вещи, которые вполне могли бы и быть в шаблоне - именно по организационным соображениям.
 

Фанат

oncle terrible
Команда форума
fixxxer
Ну, в какой-то мере это так.
Но без глобального View Controller тоже не обойтись. И мой супервизор - это как раз оно, глобальный View Controller. причём занимается оно, в отличие от "контроллеров страницы" - именно отображением, а не обработкой данных.

NeD
А как?
Можно посмотреть решение для описанной задачи? или хотя бы для упрощённого её варианта, с формой авторизации и мигающим блоком лента новостей /опрос?
Этот вопрос - сборки шаблонов сложных страниц интересует меня в первую очередь.
 

fixxxer

К.О.
Партнер клуба
Фанат
Ну да, разумеется, делается базовый класс (или иерархия), оттуда наследуемся. От того, что мы перестали делать вид, что "V" у нас только в шаблоне, и честно признались, что это View Controller, ничего не меняется =)
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Получается, нужен какой-то супервизор, который будет знать, какие блоки данных нам надо подгрузить ДО момента, когда мы будем начинать шаблонизацию.
Насколько я понимаю, этот супервизор и является основной проблемой и местом ломания копий. Собственно, противоречие сие описал Котеров ещё в прошлом веке: активный шаблон vs. пассивный.
Активный сам занимается поиском модулей, которые должны дать ему данные, а пассивный занимается только выводом, а поиском модулей занимается кто-то другой.
Тут можно вспомнить кохановский HMVC, где можно внутри шаблона сделать <?=Request::factory('controller/action/param')->execute();?> и отрендерить независимый «модуль»
 

fixxxer

К.О.
Партнер клуба
Касаемо задач, которые решаются наследованием - то же самое можно решить и на уровне вью-контроллера. Грубо говоря

$this->layout_template = 'layout.tpl';
$this->page_template = 'index.tpl';

и в layout.tpl где то include $page_template. И то же самое для всяких блочков.

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

Фанат

oncle terrible
Команда форума
PHP:
 <?=Request::factory('controller/action/param')->execute();?>
не-не-не, Девид Блейн!
именно этого я и стараюсь избежать всеми силами! Вот как раз это, в моём представлении - Адъ и Израиль.
Супервизор, в моём понимании - это Global View, который запускается, когда контроллер текущей страницы отрапортовал, что закончил работу без ошибок.
Всё то же самое вызывается в нём.

Супервизор проверяет, не переопределена ли переменная $sidebar.
Если не переопределена - дергает твой реквест-фактори, (который отрабатывает по схеме контроллер-вью и возвращает хтмл). Завернув вызов в трай.
В итоге переменная $sidebar оказывается заполнена нужным контентом/пустая.

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

NeD

Новичок
NeD
А как?
Можно посмотреть решение для описанной задачи? или хотя бы для упрощённого её варианта, с формой авторизации и мигающим блоком лента новостей /опрос?
Этот вопрос - сборки шаблонов сложных страниц интересует меня в первую очередь.
Схема примерно как fixxxer написал, но я обхожусь без пачки инклудов и if-ов
Попробую коротко описать:
При запросе на сайт, первым делам вызывается контроллер который запрашивается. Контроллер может установить нужный ему layout (по умолчанию берется из конфига). Дальше этот layout уже строит всю страницу.

Чтобы было понятней приведу небольшой пример.

Делаем запрос к сайту по урлу /admin/users/edit/1, запускается контроллер admin, который проверяет права и в случае если юзер имеет доступ к users/edit/1 запускает подмодуль admin модуля user и вызывает метод edit. Результат работы /users/edit/1 сохраняется до лучших времён :)
Дальше модуль admin запускает рендериг своего layout
Вот так выглядит layout для модуля admin
PHP:
<head>
        <?php echo action('meta/show') ?> //запускаем контроллер meta и метод show
        <?php echo action('static/css') ?>  //контроллер statis метод js
        <?php echo action('static/js') ?>
    </head>
    <body>
        <?php echo action('admin/layout')?> //здесь вставляется другой layout с менюхами, блоком юзера и т.д.
//Можно было бы его содержимое сразу сюда вписать, но таким образом я могу показать только одно окно авторизации без лишнего содержимого
 </body>
Вот так выглядит admin/layout
PHP:
 <div id="mws-header" class="clearfix">
        <div id="mws-logo-container">
            <div id="mws-logo-wrap">
                <h2 style="color:#fff;">RightCMS</h2>
            </div>
        </div>

        <div id="mws-user-tools" class="clearfix">
            <?php echo action('/admin/users/login') ?> //здесь юзер увидит приветствие и ссылку для выхода
        </div>
    </div>

    <div id="mws-wrapper">

        <div id="mws-sidebar-stitch"></div>
        <div id="mws-sidebar-bg"></div>
        <!-- Sidebar Wrapper -->
        <div id="mws-sidebar">
            <?php echo action('/admin/search') ?> //форма поиска
            <!-- Main Navigation -->
            <?php echo action('/admin/menu/render') ?> //менюха админки
        </div>
        <div id="mws-container" class="clearfix">
            <div class="container">
                <?php echo action(); ?> // а вот сюда мы вставляем результат работы users/edit/1
            </div>
            <div id="mws-footer">
                Copyright RightCMS 2012. All Rights Reserved.
            </div>
        </div>
    </div>
Получившаяся система мне нравится. Проверка прав доступа, кеширование и другие разные плюшки оформляются отдельными модулями и навешиваются на события. В результате можно спокойно навешивать дополнительный функционал, без необходимости править код старых модулей.
 

С.

Продвинутый новичок
Общий каркас верстки (шапка, подвал и т.п.):
PHP:
<html>
  <head>
    <title>{WRITE TITLE}</title>
	... какие надо еще штуки ...
    {WRITE HEAD}
  </head>
  <body>
    ... логотип, меню и все, что надо ...
    <div>
      форма авторизации/привет-ссылка на кабинет
    </div>
    <div>
      облако тегов
    </div>
    <div>
      баннерка
    </div>
    <div>
      {IF !SIDEBAR} {SIDEBAR='news-feed'} {/IF}
	  {WRITE SIDEBAR}
    </div>
    {WRITE BODY}
    ... подвал ...
  </body>
</html>
Шаблон 'contact-us':
PHP:
{TITLE='Наши контакты'}
<h1>Как можно с нами связаться</h1>
{WRITE contact_html}
<form>
  ...
</form>
Шаблон 'news':
PHP:
{TITLE='Новости полностю'}
{SIDEBAR='vote'}
<h1>Наши новости</h1>
...
Шаблон 'news-feed':
PHP:
{HEAD}
  <script src="jquery.js"></scrip>
  <!-- типа тут еще анимация есть -->
{/HEAD}
...
 

AmdY

Пью пиво
Команда форума
NeD
+1. Идеальная система, легко встраивается кеширование блоков, аджакс запросы этих блоков и даже ssi сборка.
 

stopkran

Дилетант
1. Возможные ошибки. Если мы уже начали вывод и вылезла ошибка - вывод получится рваным.
Нет. Получится нормальная страница, а на месте новостей будет красная рамка с надписью: "Тут фигня". Даже Битрикс так умеет.
 

NeD

Новичок
- схема вроде достаточно прозрачная. Даже непонятно, что там до ума доводить. Может, поделитесь, какие там подводные камни?
Так как страница собирается за 1 этап, мы не можем влиять на очередность запуска модулей. В этом главная проблем.
К примеру не совсем очевидно, как подключать только нужные ресурсы (css\js) . Нужные в данном случае это те, чьи модули отработали или отработают и требуют подключения css/js .
А до ума довожу я подобие cms на основе этой схемы, хотя это больше похоже на фреймворк с готовой админкой и стандартными модулями.
Код не документирован и нет тестов :)
Раза 3 переписывал подключение модулей и их конфиг файлы, примерно раз 5 переписывал роутинг. Вообще там много раз всё переписывалось :) Некоторые вещи менялись кардинально и приходилось бы переписывать комменты и тесты. Поэтому решил, что их (комменты/тесты), оформлю в финале.
Вот как всё это появится, так и выложу на гитхаб
 

AmdY

Пью пиво
Команда форума
NeD
эти проблемы проще оставить нерешёнными в данной парадигме, а то твоё решение сильно усложнится и утяжелится за счёт вещей, которые можно решить другим путём.
 

NeD

Новичок
NeD
эти проблемы проще оставить нерешёнными в данной парадигме, а то твоё решение сильно усложнится и утяжелится за счёт вещей, которые можно решить другим путём.
Проблему с подключением только необходимых для текущей страницы css/js можно решить, отдав часть работы ядру движка. А за модулями оставить возможность сборки/оптимизации.
Больше никаких неудобств с неконтролируемой очередностью запуска модулей у меня не возникло.
А документацию и тесты надо сделать обязательно. Правда с тестами раньше дела не имел, но начинать надо когда-то :)
 
Сверху