После установки, первоначальной настройки MODX и базового изучения Fenom, переходим к следующему шагу — созданию удобной структуры для файловых элементов вашего сайта: шаблонов, чанков, сниппетов, плагинов. Такой подход — основа для чистого проекта, облегчает поддержку, обмен и перенос сайта между разработчиками.
- Что такое шаблоны MODX?
- Что такое чанки?
- Краткий разбор синтаксиса Fenom
- Организация файловых элементов: где что хранить?
- Интеграция (перенос) готовой верстки
- Создание файловых шаблонов
- Создаём базовый шаблон base.tpl
- Шаблоны для отдельных страниц: home.tpl, blog.tpl и другие
- Вынос общих блоков из base.tpl в чанки
- Добавляем динамику
Что такое шаблоны MODX?
Шаблон (template) — это «скелет» страницы, с помощью которого контролируется базовая разметка, подключаются общие стили и скрипты, задаются места для динамических блоков (чанков, сниппетов, данных из TV).
- Каждый ресурс (страница) может использовать свой шаблон — это удобно для многоуровневых сайтов и интернет-магазинов.
- В шаблоне размещаются вызовы чанков, сниппетов, TV-переменных и системных тегов MODX/Fenom.
- Шаблон — основа для дальнейшей интеграции HTML-шаблона/дизайна.
Что такое чанки?
Чанк (chunk) — это мини-шаблон или фрагмент кода, который удобно использовать для сборки повторяющихся частей сайта: меню, подвал, боковые панели, формы, карточки и т.д.
Основные плюсы:
- Вся разметка лежит централизованно, редактируется в одном месте и минимизируются ошибки.
- Легко вынести в файл для лучшей структуризации и ускорения разработки (особенно в крупных проектах).
- Идеально интегрируются через Fenom —
например{include 'chunks/header.tpl'}
Краткий разбор синтаксиса Fenom
Для тех кто забыл или не делал ДЗ из прошлого урока, да и повторение — мать учения.
Главное, что нужно запомнить: шаблонизатор активируется открывающей фигурной скобкой {, а деактивируется закрывающей }.
Плейсхолдеры в чанках сохраняют свою функциональность, но записываются иначе. Сравним синтаксис родного парсера MODX (modParser) и Fenom.
| MODX (modParser) | Fenom |
|---|---|
[[+id]] |
{$id} |
[[+id:default=`test`]] |
{$id ?: 'test'} |
[[+id:is=``:then=`test`:else=`[[+pagetitle]]`]] |
{$id == ''? 'test': $pagetitle} |
Для работы с ресурсами, настройками и другими элементами MODX в Fenom используется служебная переменная $_modx, предоставляющая безопасный доступ к основным методам и свойствам.
| MODX (modParser) | Fenom |
|---|---|
[[*id]] |
{$_modx->resource.id} |
[[*tv_param]] |
{$_modx->resource.tv_param} |
[[%lexicon]] |
{$_modx->lexicon('lexicon')} |
[[~7]] |
{$_modx->makeUrl(7)} |
[[~[[*id]]]] |
{$_modx->makeUrl($_modx->resource.id)} |
[[++system_setting]] |
{$_modx->config.system_setting} |
Fenom и MODX-теги могут использоваться вместе в одном шаблоне, но обработка происходит в два этапа — сначала выполняются теги Fenom, затем MODX.
Организация файловых элементов: где что хранить?
В первом уроке мы говорили, что будем использовать файловые элементы (шаблоны, чанки, сниппеты, плагины), чтобы проект был структурированным и удобным для разработки.
По умолчанию каталог для файловых элементов — {core_path}elements/ — т.е. в папке с ядром системы. Лично мне это не нравится, поэтому давайте изменим к ним путь, делается это в системной настройке pdotools_elements_path: замените ее значение с {core_path}elements/ на {assets_path}elements/.

Далее нам нужно создать папку elements (в директории assets) — это место хранения наших файловых чанков, шаблонов и т.д. Далее для удобства в assets/elements/ создайте отдельные папки для каждого типа элементов вашего сайта:
- Шаблоны:
assets/elements/templates/ - Чанки:
assets/elements/chunks/ - Сниппеты:
assets/elements/snippets/
В итоге ваша структура должна выглядеть так:
/assets/elements/
├── templates/
├── chunks/
├── snippets/
Интеграция (перенос) готовой верстки
Делать все будем на основе готового HTML шаблона. Я буду использовать официально купленный шаблон Porto. Вы можете использовать любой (логика разработки от этого не меняется). Ну или на свой страх и риск поискать nulled версию данного шаблона (попадаются не самые свежие но все же).
Давайте сначала рассмотрим из чего вообще состоят шаблоны:

По факту это html файлы и папки с css, js и изображениями + различные php обработчики и т.п. Нам нужно перенести только css, js и изображения (у кого есть шрифты, то их тоже переносим). В общем берем и заливаем их в корень сайта (в моем случае, это папки css, js, vendor). Сделать это можно через файловый менеджер хостинга или ftp клиент.
Важно. Часто вижу шаблоны у которых все нужные папочки находятся в папке assets — тогда также переносите их в assets.
Создание файловых шаблонов
Это все можно делать как с админки, так и при помощи IDE, но статей о настройке IDE здесь пока нет, поэтому держите с ресурса web-revenue.ru: MODX: Работа с файловыми элементами (настройка нескольких способов: через IDE — PhpStorm, бесплатный редактор кода — Visual Studio Code (тоже можно назвать IDE, т.к. есть терминал и т.п.) и админку самого движка).
PS. Я буду использовать PhpStorm.
Создаём базовый шаблон base.tpl
Для организации сквозных элементов сайта заведите главный базовый файл base.tpl. В нём размещаются общие части для всех страниц — хедер, футер, основная структура, подключение CSS/JS.
И поместите в него пока весь код главной страницы (в моем случае это код из файла demo-seo-2.html).


После этого можно зайти в админку и проверить появился ли файл с кодом.

Появился, все норм, едем далее.
Шаблоны для отдельных страниц: home.tpl, blog.tpl и другие
Для главной страницы создайте файл home.tpl, со следующим кодом:
{extends 'file:templates/base.tpl'}
{block 'content'}
{/block}
Здесь, через {extends ...} мы подключаем базовый шаблон, а в block 'content' нужно подключить остальной контент так сказать. Сейчас нам нужно вырезать не сквозные элементы и поместить их в block 'content'.
В моем случае, это все что внутри:
<div role="main" class="main"> здесь все вырезаем </div>

И вставляем его в шаблон домашней страницы. Должно получится примерно следующее:

Сохраняем его и снова возвращаемся в base.tpl и на месте вырезанного кода пишем конструкцию:
{block 'content'}
{/block}

Соответственно все сохраняем и давайте подключим наш файловый шаблон к MODX. Делать это будем через админку: идем на вкладку Элементы — Шаблоны (там у нас сейчас 1 шаблон — начальный), открываем его и вместо родного кода подключаем шаблон главной страницы, вот такой конструкцией:
{include 'file:templates/home.tpl'}
После можно переименовать его например в home, далее сохраняем его и переходим на главную страницу.

Если в место главной увидели просто белый лист — не пугайтесь, где то какие то фигурный скобки (у Fenom с ними конфликт, т.к. он сам их использует), в общем открываем журнал ошибок и смотрим на что ругается.

Далее находим эти строчки (CTRL + F):

и правим: после { ставим пробел, и перед } тоже ставим пробел, проще всего это сделать через поиск и замену (Ctrl + F / R)

И по такой же логике для закрывающихся фигурных скобок.

Сохраняем и снова проверяем главную (CTRL + F5), если она снова пустая. Идем снова в журнал ошибок и смотрим новые.
![]()
У меня это уже ошибки в базовом шаблоне, правим их точно также, пробелами и сохраняем шаблон. После устранения конфликтов у вас должна открыться домашняя страница.

Запутанно немного, наверное) Кто то скажет нафига этот Fenom, геморрой с фигурными скобками и т.п. На самом деле это все правится очень быстро, с опытом сразу же видишь эти скобки и сразу через поиск и замену правишь их, потом уже переносишь код, да и далеко не во всех шаблонах они есть. В принципе это единственный минус, а во всем остальном одни плюсы, а самое главное многие Fenom конструкции нельзя сделать при помощи modparser — надо писать сниппеты и т.п.)
Идем далее, нам еще нужно создать 5 шаблонов: services.tpl, services_detail.tpl, contacts.tpl, blog.tpl, blog_post.tpl (это столько у нас демок так сказать).
Внимательные думаю заметили — в именах файлов не
-, а_. Это еще одна особенность Fenom, он не всегда дружит с-.

Логика как у home.tpl, т.е. создаем файловый шаблон, помещаем в него:
{extends 'file:templates/base.tpl'}
{block 'content'}
Здесь код не сквозных элементов, которые внутри <div role="main" class="main">...</div>
{/block}

Сразу правим конфликты с фигурными скобками — если они присутствуют в коде. Сохраняем и так создаем остальные файловые шаблоны.

После этого создаем в админке новые шаблоны и вызываем в них наши файловые.
{include 'file:templates/services.tpl'}
{include 'file:templates/services_detail.tpl'}
{include 'file:templates/contacts.tpl'}
{include 'file:templates/blog.tpl'}
{include 'file:templates/blog_post.tpl'}

Можно сделать все на 1м шаблоне, с помощью специальных конструкций, или даже просто блоков (будем создавать в одном из следующих уроков).
Вынос общих блоков из base.tpl в чанки
Вообще я как то не очень люблю дробить шаблоны на отдельные чанки и т.п. (и делать этого не буду). Но вдруг вам так удобнее (хотите сократить код базового шаблона. путем его дробления), тогда можете к примеру создать вот такие чанки:
- Меню →
chunks/menu_main.tpl - Хедер →
chunks/header.tpl - Футер →
chunks/footer.tpl - Боковые панели, подвал, кнопки — отдельными чанками
Перенести код к них, а затем в шаблоне (base.tpl) для подключения чанка используйте:
{include 'chunks/header.tpl'}
{include 'chunks/menu_main.tpl'}
{include 'chunks/footer.tpl'}
Такой подход позволяет хранить каждый элемент разметки сайта отдельно и централизованно. Например:
/assets/elements/templates/ - base.tpl - home.tpl - blog.tpl /assets/elements/chunks/ - header.tpl - menu-main.tpl - footer.tpl
Благодаря такой структуре вы сможете легко менять дизайн, контент и расширять проект — достаточно поправить чанк или шаблон в нужной папке.
Добавляем динамику
Сейчас у нас по факту статические шаблоны и управлять ими можно пока только, через код. Давайте частично исправим (это пока) и добавим немного синтаксиса Fenom в базовый шаблон (base.tpl). Только мета теги так сказать для начала и вот так начальный код преобразится.
{set $site_name = 'site_name' | config}
{set $site_url = 'site_url' | config}
{set $culture = 'cultureKey' | config}
{set $title = ($_modx->resource.longtitle ?: $_modx->resource.pagetitle) | notags}
{set $description = $_modx->resource.description | replace :' "':' «' | replace :'"':'»'}
<!DOCTYPE html>
<html lang="{$cultureKey}">
<head>
<base href="{$site_url}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{$title}</title>
<meta name="description" content="{$description}">
<meta name="author" content="fastdevlab.com">
<!-- Favicon --> и далее весь остальной
Давайте расшифрую его:
{set $site_name = 'site_name' | config}
{set $site_url = 'site_url' | config}
{set $culture = 'cultureKey' | config}
Что делает:
- Берёт значения системных настроек
site_name,site_urlиcultureKey. - Сохраняет их в переменные
$site_name,$site_urlи$cultureKey
Эквивалент в PHP:
$site_name = $modx->getOption('site_name');
$site_url = $modx->getOption('site_url');
$cultureKey = $modx->getOption('cultureKey');
Это делается не для того, чтобы «заработало», а чтобы код был чище, быстрее и удобнее.
1) Производительность (небольшая, но есть)
Каждый обычный вызов вызов:
{'cultureKey' | config}
или
{$_modx->config.cultureKey}
— это обращение к конфигу.
Если ты вызываешь одно и то же значение 5–20 раз — ты 5–20 раз дергаешь MODX.
Когда сохраняешь в переменную:
{set $culture = 'cultureKey' | config}
то дальше используешь значение из памяти Fenom:
<html lang="{$culture}">
— уже без обращения к MODX.
2) Читаемость
Вместо длинных и визуально тяжелых конструкций:
{$_modx->config.site_name}
{'site_url' | config}
ты пишешь:
{$site_name}
{$site_url}
Читается быстрее и проще.
3) Единая точка управления
Если нужно поменять источник, фильтры или логику — меняется только строка с set.
Например:
- хочешь фильтр trim добавить
- хочешь fallback сделать
- хочешь заменить кавычки
Достаточно изменить 1 строку, а не искать 10 вызовов по шаблону.
4) Упрощает условия
Например, в условии проще использовать:
{if $culture == 'ru'}
чем:
{if ('cultureKey' | config) == 'ru'}
5) Так делают во всех профессиональных Fenom-шаблонах
Просто потому что это:
- чище;
- короче;
- поддерживаемее;
- быстрее;
Итог в одну фразу: можно вызывать конфиг напрямую — это нормально, но сохранение в переменные делает код более быстрым, чистым и удобным для поддержки.
2. Формируем заголовок страницы (<title>)
{set $title = ($_modx->resource.longtitle ?: $_modx->resource.pagetitle) | notags}
Что делает: берёт longtitle, если он заполнен. Иначе использует pagetitle + удаляет HTML-теги через | notags
Эквивалент PHP:
$title = strip_tags($resource->get('longtitle') ?: $resource->get('pagetitle'));
3. Формируем description (мета-описание)
{set $description = $_modx->resource.description
| replace :' "':' «'
| replace :'"':'»'
}
Что делает:
- Берёт поле description у ресурса;
- Меняет комбинацию пробел + кавычка ( «) на пробел + «;
- Меняет все двойные кавычки («) на русскую закрывающую кавычку »;
То есть текст Мой "текст" станет Мой «текст».
4. Устанавливаем язык страницы
<html lang="{$cultureKey}">
Берёт системную настройку cultureKey, например: ru, en, de.
5. Base URL
<base href="{$site_url}">
- Устанавливает корень сайта;
- Избегает путаницы с относительными ссылками.
6. Вставляет meta-теги
<title>{$title}</title>
<meta name="description" content="{$description}">
<meta name="author" content="fastdevlab.com">
Тут всё очевидно: вывод переменных $title и $description.
Человеческая расшифровка одним абзацем.
Код берет настройки MODX (site_name,site_url,cultureKey), формирует SEO-оптимизированный заголовок и description страницы, конвертирует кавычки в русские, устанавливает язык страницы, выводит мета-теги и ставит<base href>, чтобы все ссылки в шаблоне работали предсказуемо.
Ну давайте сразу испытаем работает наш немного обновленный код или нет. Идем в нашу пока единственную страницу, заполняем поля longtitle, description, сохраняем и смотрим.

В яндекс браузере сразу вижу, то подтянулся title.

Давайте откроем код: CTRL + U. Все должно быть заполнено.

На сегодня думаю достаточно. В следующий урок будет попроще и покороче, будем выносить повторяющиеся поля (ну или сквозные: телефоны, соц сети и т.п. в системные настройки (при помощи компонента ClienConfig), и затем выводить их в нашем базовом шаблоне.
Если вам что-то не понятно, welcome в комментарии)








