Организация проекта в MODX Revolution

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

Так же, довольно хреново, что нет никаких стандартов или рекомендаций от разработчиков по структуре проекта в MODX, стандарты кода вот есть, а организации — хрен, пишите как хотите. Ну вот и пишут. Обойдя взглядом несколько популярных компонентов, хочется взять и договориться хотя-бы о единообразии именования чанков, не говоря уже о параметрах сниппетов.

Проблема

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

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

Решение

Сначала я старался смотреть, как делают другие, в частности, больше всех наверное повлиял товарищ Безумкин с его минишопом, но за пару лет работы с MODX у меня выработалась своя определенная методология, которую я пытаюсь прививать окружающим, дабы не плодить потом нерасширяемое говно. Собственно, структура ни разу не претендует на Оскар, не является серебряной пулей и вообще, все зависит только от вас.

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

Чанки

Начинаю с чанков я не случайно. Так как верстка у нас в БЭМ, хорошо бы сохранить ее целостность и на бэкенде. В первую очередь нам понадобятся две новые категории: Blocks и Bundles, нужны они просто для удобства, чтобы не плодить 100500 чанков в корне. Те, кто знаком с БЭМ, думаю знают что такое блоки в контексте методологии.

Блоки — некая автономная единица сайта, кирпичик, например: форма поиска или меню. Каждый блок находится в своей категории, внутри категории Blocks. Именуются блоки по шаблону b.CatalogItem. Префикс b. нужен, чтобы быстро отличить вызов чанка блока от чего-то другого. Если нужно вынести элемент блока в отдельный чанк, например, для шаблонизации, именуется он так: b.CatalogItem.image. Модификаторы блока или элемента в отдельных чанках мы пишем следующим образом: b.CatalogItem.modSizeBig.image или b.CatalogItem.image.modSizeBig, в зависимости от того, к чему применен модификатор.

Бандлы — это группы блоков, создающих собой единое целое. Если предполагается, что где-то в одном месте будет сконцентрировано большое количество блоков и при этом эта группа никак изменяться не будет, то блоки группируются в бандл. Туда можно отнести, например, сложную верстку хедера или футера. Однако, в бандлы нельзя выносить контент, он должен находиться в шаблоне или блоке. Принцип именования прост: bundle.BundleName. С количеством рекомендую не увлекаться, у меня их обычно 2–4 штуки.

Плюсом ко всему этому я держу категорию Base, в которой у меня лежит head и подключаемые скрипты: base.Head и base.Scripts. Вроде в бандлы выносить как-то не логично, пусть будет так.

Для наглядности, приведу пример организации блога на этом сайте.

  • b.Blog — блок, содержащий контейнер и вызывающий, скажем getResources, выводящий непосредственно список постов
  • b.Blog.post — элемент блока, содержащий анонс записи в блоге, прописывается в параметре tpl у getResources

Шаблоны

Здесь мы по сути собираем страницу по кирпичикам. Кирпичиками, как не сложно догадаться, являются блоки. Шаблоны, относящиеся к одной сущности должны как либо группироваться. Я использую некое подобие неймспейсов, ставя перед именем шаблона префикс группы. Префикс от имени отделяется точкой. Если нагляднее, имя шаблона будет выглядеть как-то так: group.TemplateName. На случай, если есть два однотипных шаблона в одной группе, например, для обычного и премиум товара, к имени так же добавляется суффикс. Ну и немного реальных примеров:

  • blog.Index — шаблон со списком постов в блоге
  • blog.Post — шаблон с полным текстом поста

Еще могут быть, например:

  • catalog.List — шаблон, отображающий список товаров
  • catalog.Item.default — шаблон с полной информацией о товаре
  • catalog.Item.premium — шаблон какого-нибудь премиум товара

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

<!DOCTYPE html>
<html>
  [[$base.Head]]
  <body class="b-page">
    [[$b.Header]]
    <div class="b-content">
      <div class="b-content__container l-container">
        [[$b.Blog]]
      </div>
    </div>
    [[$b.Footer]]
  </body>
</html>

Дополнительные поля

Первым делом, создаем общие поля с максимально удобным именем. К общим, например, относится image, так как может применяться к абсолютно любому ресурсу. Так же, для каждого шаблона может быть свой набор уникальных полей со своими настройками, чтобы не путаться и случайно не прилепить поле, настроенное для одного шаблона к другому, я называю поля просто исходя из имени шаблона: catalog.Item.image, catalog.Item.price.

Так же, бывает, что поле имеет не шаблон, а блок, который используется в нескольких шаблонах. В этом случае поля именуются уже исходя из имени блока. Учитывая то, что значение поля будет в каком-нибудь элементе, название поля для блока просто дублирует имя блока или элемента: b.CatalogItem.image, b.CatalogItem.price.

Вызов такого поля в шаблоне может быть будет не совсем кошерный, скорее даже длинный и страшноватый, но привыкнуть можно. Зато сразу все понятно и отпадает проблема придумывания имен, можно насоздавать хоть 100500 полей, не боясь пересечься с другими.

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

  • blog.Post.origin — содержит ссылку на источник, если запись была откуда-то позаимствована
  • blob.Post.tags — теги поста
  • blog.Post.premium — булево, обозначающее премиум-пост, например, на случай организации платной подписки

Заключение

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