Грамотная организация view с поддержкой мультисайтовости и скинов

Koc

Новичок
Добрый вечер. Возникла необходимость делать несколько сайтов, у которых будет одна база но разные темы оформления. Некоторые модули будут пересекаться, но будут и свои, уникальные. Накидал вот такую схемку, вдохновлялся из Symfony2 и Magento

Код:
Путь к шаблону
app/%app_name%/templates/%area_name%/%theme_name%/%module_name%/%format%/%template_name%.%renderer%

%app_name%:      default/site1.com/site2.com
%area_name%:     admin/frontend/install (превед, Magento)
%theme_name%:    default/mobile/winter (зимой, к примеру, другое оформление)
%module_name%:   user/forum/blog
%format%:        html/xml
%template_name%: edit_form/list/search
%renderer%:      twig/phtml

Часть "app/%app_name%/templates/%area_name%/%theme_name%/" - это путь к папке с шаблонами. Таких путей несколько (Так как приложений и тем тоже несколько).
Методы контроллера возвращают Response (превед, Symfony2)
PHP:
// упрощенный код
class User extends Controller_Frontend_Abstract
{
  public function viewAction()
  {
    return $this->render($layout = '%module_name%:%template_name%.%format%.%renderer%', array $data = array());
  }
}
Код:
Например, просмотр пользователя
%app_name%=site1->default (приложение наследуется от другого)
%theme_name%=winter
%area_name%=frontend
$this->render($layout = 'user:layouts/profile.html.twig', $user);

// происходят попытки прочитать следующие файлы с шаблонами (как только нашли шаблон по указанному пути - останавливаем цикл)
app/site1/templates/frontend/winter/user/layouts/html/profile.twig
app/site1/templates/frontend/default/user/layouts/html/profile.twig (допустим, в теме winter файла profile.twig не оказалось. Тогда берем из дефолтной)

app/default/templates/frontend/winter/user/layouts/html/profile.twig (а в дефолтной теме его тоже не было, идем в родительское приложение и ищем в нем. Но тут есть ограничение - модуль User должен быть в родительском приложении. Если его нет - сюда не идем)
app/default/templates/frontend/default/user/layouts/html/profile.twig
Итак, если у вас хватило терпения, то может быть есть какие-нибудь вопросы/замечания? Что в этой схеме можно было бы улучшить, с какими трудностями я столкнусь? Как вы делали?

Предположим, что в этой схеме все нормально и мы ее принимаем. Тогда возникают вопросы у меня:
1) Где должны подставляться плейсхолдеры (%app_name%, ...)? Ну некоторые мы заполняем в методе render контроллера, но остаются следующие, которые входят в путь папки с шаблонами:

%app_name%
%area_name%
%theme_name%

С %area_name% все просто, ее можно установить равной frontend в Controller_Frontend_Abstract::preDispatch(). %theme_name% - может зависеть от настроек пользователя (несколько стилей оформления на форуме) и от настроек приложения. Может быть какой-нибудь eventDispatcher тут поможет? С %app_name% все еще сложнее, так как нужно проверять, есть ли такой модуль в родительском приложении.

2) В будущем я планирую сделать двупроходный рендеринг: сначала собирается body а потом вся страница. Внутри body будут находиться всеразличные виджеты (аналог Blocks из Magento, которые должны будут иметь доступ к head, что бы добавлять css/js). Куда в моей схеме подставить лейаут для второго прохода? По идее этот двупроходный рендеринг нужен только для html, какому-нить xml/json это вовсе не нужно. Как бы этот момент красиво разрулить?

3) Как организовать assets (css/js/img)? Тут у меня вообще идеи закончились. Есть общие js-ники, которые просто привязываются к модулю, есть специфичные для темы js-ники. Как это разруливать?
 

HraKK

Мудак
Команда форума
Каждый сайт должен иметь свой бутстрап. Там и определяй Template патч.
http://paymefo.com/wiki/doku.php?id=structure:application
Хотя я бы советовал вообще весь мой код изучить)

Где должны подставляться плейсхолдеры (%app_name%, ...)?
В рендере.

Методы контроллера возвращают Response (превед, Symfony2)
Фигасебе, симфони идет по моему пути. Скоро догонит.

Как бы этот момент красиво разрулить?
http://paymefo.com/wiki/doku.php?id=structure:render
Разгуливается в 0 проходов.

Как организовать assets (css/js/img)? Тут у меня вообще идеи закончились. Есть общие js-ники, которые просто привязываются к модулю, есть специфичные для темы js-ники. Как это разруливать?
Я не стороник JS и т.д. подключать в коде. Подумай над этим.
 

HraKK

Мудак
Команда форума
Mr_Max
Фреймворк, в цмс такого нету там стандартный MVC - хуле делал года 4 назад.
 

AmdY

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

HraKK

Мудак
Команда форума
AmdY
Магенто дерьмо, старая по крайней мере. Кстати, один из lead core разработчик магенто признал что моя цмс круче)

В ооп ничего нету мешающего и сложного нету, если с умом подходить, а не по ламерски.
 

Koc

Новичок
Каждый сайт должен иметь свой бутстрап. Там и определяй Template патч.
бутстрап есть. Просто инстанс вью создается не сразу (он может быть в момент вызова controller::render только впервые создаться каким-нибудь контейнером). Вообще довольно занятный код, есть что посмотреть, спасибо.

Я не стороник JS и т.д. подключать в коде. Подумай над этим.
так это и не в коде будет подключаться а в шаблонах/лейаутах.

HTML:
{{ asset.js('path/to/jquery.js') }}
{{ asset.css('path/to/file.css', 'revision-of-this-file') }}
<!-- ревизия нужна так как эти css-ники допустим будут кешироваться на клиенте. Обновили css-ник, изменили его ревизию в лейауте - на клиенте он обновился -->
— да, очень радует. Частично в symfony (1.x) это тоже есть, но forward'ы и редиректы там через exception'ы реализованы, не нравится мне это.
c redirect все понятно, а forward мимо response должен идти имхо.

PHP:
AppDispatcher
  DISPATCH:
    $result = $controller->process();
    if ($result instanceof Forward) {
      // set another m/c/a
      goto DISPATCH;
    }
    if ($result instanceof Response) {
      $result->send();
      // and exit
    } else {
      throw new e('Какой-то галимый ответ от контроллера');
    }

заоопэшились вы товарищи, зачем так усложнять. Magento - дерьмо и брать с него пример дебильство, делаете гибкость нафик никому не нужную, только мешающую.
что значит никому не нужную гибкость? Надо мной стоит заказчик, которому нужна вот такая мультисайтовая система с поддержкой тем. Я че - сам что ли это все придумываю от нефиг делать? Это реальная задача.

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

fixxxer

К.О.
Партнер клуба
Обойтись без говнокода вида if foo instanceof bar и уложить всю возможную логику в интерфейс респонса, так чтобы он оставался респонсом.
 

Вурдалак

Продвинутый новичок
Ну, назовите это Internal_Response. Он уже может быть обычным Response или Forward. Проблема в этом? :D
 

Koc

Новичок
Вурдалак
та все нормально, с этим проблем нет). Расскажите лучше как assets организовать. Сегодня кстати Фабьен удалил их из Symfony2.
 

fixxxer

К.О.
Партнер клуба
Я не выпендриваюсь и делаю просто:

{{ url static '/css/style.css' }}

Ревизию само дописывает - UrlBuilder про нее в курсах.
 

AmdY

Пью пиво
Команда форума
fixxxer
+1, кстати, это по сути является и ответом на первый вопрос,
только вместо url builder будет отрабатывать path builder, который знает о текущем модуле и названии шаблона
в то же время twig вроде как обладает наследованием и это решение проблемы с номер два про два прохода. Сразу расчитывается модуль, открывается его шаблон, а там указан Master Template.
Для html - MT будет с head,body....
Для ajah запроса - MT просто вывод результата наследника, без вских шапок
Для ajaj и ajax - MT содержит вызов функции сериализующей данные шаблона в json или xml

Если не загоняться, то все решения лежат на поверхности.
p.s. Шет, нужно срочно работу менять, а то сам черствею и поделиться знаниями не с кем :(
p.p.s. вот про наследование http://twig.kron0s.com/blog/2010/06/14/living-on-the-edge-dynamic-and-conditional-inheritance/
 

fixxxer

К.О.
Партнер клуба
у меня так и сделано =) прямо в точности =)

только для чистого json-ответа вообще нет шаблонов, другая вьюха которая отдает чистый json_encode себя
 

Koc

Новичок
AmdY
нее. 2 прохода это так:

1 проход, собирается body
PHP:
// controller
$this->render('user:profile', array('user' => $user));
HTML:
{# template #}
<div class="left">
  {{ user.name }}
  {% if user.isMan() %}
    мужик
  {% else %}
    баба
  {% endif %}
</div>
<div class="right">
  {{ widget('groups:users_groups', {'template' : 'foo/bar.phtml', 'user' : user}).render() }} {# мы вызываем виджет - это что-то типа маджентовского блока #}
</div>
PHP:
class Widget_Users_Groups
{
  public function setUser(User $user);

  public function toHtml()
  {
    $groups = $this->contier->orm_entity_manager()->get('read')->select('Groups g')->join('Groups2User gu')->where('gu.user_id = :user_id')->bind('user_id', $this->user->getId());

    return $this->render($this->template, $groups);
  }
}
и допустим этому виджету понадобилось что-то добавить в head (js/css/rss/...). Он добавляет это в реестр через шаблон за первый проход. А во втором проходе, когда мы собираем уже всю страницу, с head, берем эти данные из реестра.
 

Вурдалак

Продвинутый новичок
Во-первых, это наиболее естественно: контроллер как раз занимается преобразованием request → response.
Во-вторых, как следствие из первого, прерывание работы контроллера при redirect/forward завершается наиболее просто: с помощью return. И никаких там exit и exception'ов.
 
  • Like
Реакции: Koc
Сверху