# Как AVE.cms собирает сайт
Эта глава объясняет публичную модель AVE.cms: что такое шаблон сайта, рубрика,
поле, документ, навигация, запрос, блок и каталог, как они связаны и в каком
порядке превращаются в готовую HTML-страницу.
## Общая схема
```text
URL
-> документ
-> рубрика
-> поля документа
-> шаблон рубрики
-> [tag:maincontent]
-> шаблон сайта
-> блоки и модули
-> запросы
-> навигации
-> SEO и хлебные крошки
-> готовый HTML
```
У каждой сущности одна основная зона ответственности:
| Сущность | Назначение |
| --- | --- |
| Шаблон сайта | Общая HTML-оболочка: `head`, шапка, подвал и места для компонентов. |
| Тема | Статические CSS/JS, изображения, шрифты и порядок их подключения. |
| Рубрика | Тип контента: набор полей, правила URL, права и способ вывода одного документа. |
| Поле | Контракт отдельного значения: редактор, валидация, хранение и публичный вывод. |
| Документ | Конкретная запись рубрики со своим URL, состоянием, SEO и значениями полей. |
| Навигация | Независимое дерево ссылок на документы и произвольные URL. |
| Запрос | Сохраненная выборка документов с условиями, сортировкой и шаблоном списка. |
| Блок | Переиспользуемый фрагмент разметки или логики. |
| Каталог | Иерархия разделов, назначение документов, характеристики, фильтры и листинги. |
| Модуль | Устанавливаемое расширение с маршрутами, тегами, таблицами, hooks и интерфейсом. |
## Практические руководства
Если вы впервые работаете с системой или хотите понять новый редактор целиком,
начните с подробной главы [Content Studio: от рубрики до публикации](content-studio.md).
| Раздел панели | Руководство |
| --- | --- |
| Документы | [Создание, публикация, URL, ревизии и системные страницы](documents.md) |
| Рубрики и поля | [Проектирование типа контента, формы и публичного вывода](rubrics.md) |
| Шаблоны | [Внешняя оболочка сайта, теги, кеш и ревизии](templates.md) |
| Twig-компоненты | [Точечные модульные вставки и границы их применения](twig-components.md) |
| Темы | [CSS, JavaScript, изображения, шрифты и ZIP-пакеты темы](themes.md) |
| Блоки | [Переиспользуемые фрагменты, редакторы и параметры](blocks.md) |
| Навигация | [Шаблоны уровней, пункты и сборка HTML](navigation.md) |
| Запросы | [Выборки документов, группы условий и шаблоны списка](requests.md) |
| Каталог | [Универсальные деревья разделов, поля и фильтры](catalog.md) |
Медиа, поля и модули вынесены в отдельные главы: [Медиа и
миниатюры](../media/README.md), [Поля документов](../fields/README.md) и
[Модульная система](../modules/README.md).
Основной публичный сайт собирается встроенными сущностями AVE.cms. Twig не
заменяет шаблоны сайта, рубрик и запросов. Для сложных модульных компонентов он
подключается через обычный зарегистрированный тег; подробности приведены в
[отдельной главе](twig-components.md).
## Три уровня шаблонов
В AVE.cms слово «шаблон» используется для трех связанных, но разных уровней.
### 1. Шаблон сайта
Шаблон сайта содержит весь HTML-документ:
```html
[tag:title]
[tag:rubheader]
[tag:navigation:main]
[tag:maincontent]
[tag:rubfooter]
```
Главный контракт шаблона сайта — тег `[tag:maincontent]`. В него движок
подставляет результат шаблона рубрики текущего документа или HTML системной
страницы модуля.
Шаблон сайта обычно содержит:
- ``, `` и ``;
- SEO-теги текущего документа;
- общую шапку и подвал;
- теги подключения CSS и JavaScript активной темы;
- основную и вспомогательные навигации;
- общие блоки и модульные компоненты;
- `[tag:rubheader]` и `[tag:rubfooter]` для точечных вставок рубрики.
Шаблон сайта не должен знать полный набор полей новости, товара или статьи.
Этим владеет рубрика.
### 2. Основной шаблон рубрики
Основной шаблон рубрики описывает содержимое одного документа:
```html
[tag:doc:document_title]
[tag:fld:lead]
[tag:fld:image]
[tag:fld:content]
```
После обработки полей этот HTML становится `[tag:maincontent]` внешнего
шаблона сайта.
### 3. Дополнительный шаблон рубрики
Документ может выбрать альтернативный вариант вывода той же рубрики. Например,
у рубрики «Статьи» могут быть шаблоны:
- обычная статья;
- лонгрид;
- фотогалерея;
- промостраница.
Набор полей и правила рубрики остаются общими, меняется только композиция
конкретного документа.
## Рубрика
Рубрика — это тип контента, а не папка документов. Примеры рубрик:
- Новости;
- Статьи;
- Страницы;
- Товары;
- Сотрудники;
- Вопросы и ответы.
Рубрика определяет:
- набор и группы полей;
- порядок и ширину полей в редакторе документа;
- значения полей по умолчанию;
- основной и дополнительные публичные шаблоны;
- шаблон тизера для элементов запросов;
- внешний шаблон сайта;
- шаблон формирования URL новых документов;
- права групп пользователей;
- Open Graph, вставки в header и footer;
- код и hooks до и после сохранения документа.
Например, рубрика «Новости» может содержать поля `image`, `lead`, `content`,
`source` и `gallery`. Все документы этой рубрики используют один контракт
данных, поэтому запросы и шаблоны могут обращаться к полям по стабильным alias.
Подробнее о типах, настройках и хранении значений: [Поля документов](../fields/README.md).
## Документ
Документ — конкретная запись выбранной рубрики. Он хранит:
- ID и рубрику;
- название и заголовок хлебных крошек;
- alias и историю URL;
- публикацию, удаление и срок действия;
- автора и даты;
- анонс;
- SEO, robots, ключевые слова и теги;
- значения полей рубрики;
- выбранный дополнительный шаблон рубрики;
- связи с каталогами и модулями.
Документ предоставляет данные, но не должен самостоятельно описывать всю
внешнюю оболочку сайта.
### URL документа
Рубрика может формировать alias по шаблону:
```text
news/%year/%month/%alias
```
Для документа с коротким alias `company-opened-office` результатом будет:
```text
/news/2026/07/company-opened-office
```
Меню не участвует в формировании URL. После изменения alias прежний адрес может
остаться в истории редиректов и вести на новый URL.
## Навигация
Навигация — независимое дерево ссылок. Она хранит:
- пункты и уровни вложенности;
- связь с документом или произвольный URL;
- название, описание, target и изображение;
- CSS-класс и дополнительные атрибуты;
- доступность группам пользователей;
- шаблоны обычного и активного пункта каждого уровня.
Навигация вставляется в страницу по alias:
```text
[tag:navigation:main]
```
### Как собирается HTML навигации
Шаблоны навигации применяются в строгом порядке:
```text
Обёртка: начало
Уровень 1: начало
Обычный или активный пункт уровня 1
...
Уровень 1: конец
Обёртка: конец
```
Для вложенного меню шаблон пункта родительского уровня должен содержать место
вывода следующего уровня:
```html
```
`Уровень N: начало` задаёт обёртку списка. Тег `[tag:content]` внутри неё
заменяется готовыми пунктами уровня. Если тег не указан, пункты добавляются
после начальной разметки. `Уровень N: конец` всегда выводится после всех пунктов
уровня. Поля `Обёртка: начало` и `Обёртка: конец` выводятся один раз вокруг
всего дерева.
Пример настроек первого уровня:
```html
```
Результат будет иметь полную структуру
``. Если в навигации нет доступных пунктов,
внешние обёртки не выводятся.
Удаление пункта меню не удаляет документ и не отключает его URL. Один документ
может находиться в нескольких меню или не входить ни в одно.
## Запросы
«Запрос» в AVE.cms — не HTTP-запрос, а сохраненная выборка документов. Он
отвечает на два вопроса:
1. Какие документы выбрать?
2. Как вывести список и каждый его элемент?
Запрос может задавать:
- одну или несколько рубрик;
- простые и вложенные группы условий;
- сочетания `И` и `ИЛИ`;
- сортировку;
- лимит и пагинацию;
- шаблон элемента;
- обертку списка;
- настройки кеширования.
Примеры запросов:
- последние новости;
- популярные статьи;
- материалы текущего автора;
- связанные документы;
- акции;
- товары с заданными свойствами.
Тег запроса использует ID или alias:
```text
[tag:request:latest_news]
```
Условия с разной логикой должны быть сгруппированы явно:
```text
Опубликован
И
(
Рубрика = Новости
ИЛИ
Рубрика = Статьи
)
И
(
Тег = Важное
ИЛИ
Просмотры > 1000
)
```
Система сначала выбирает документы, затем применяет к каждому шаблон элемента и
объединяет элементы в обертку списка.
## Блоки
Блок — переиспользуемый фрагмент, который можно вставлять в шаблон сайта,
шаблон рубрики, запрос или другой блок.
Примеры:
- контакты в подвале;
- баннер;
- форма подписки;
- предупреждение;
- преимущества;
- сложный компонент с PHP-логикой.
```text
[tag:sysblock:product_card]
[tag:sysblock:contacts]
```
Блоки могут содержать запросы, навигации, модульные теги и условия. Для новой
логики предпочтительнее передавать сложные операции в сервисы и модули, оставляя
в блоке композицию и небольшие проектные условия.
## Каталог
Каталог — отдельный слой над рубриками, документами и полями. Он не равен
магазину и не должен предполагать, что каждый проект содержит товары.
Каталог можно использовать для:
- товарного каталога;
- новостного портала;
- базы знаний;
- каталога услуг;
- библиотеки документов;
- базы специалистов или организаций;
- любого структурированного раздела с фильтрацией.
### Место каталога в системе
```text
Рубрики и поля
|
v
Документы
|
v
Каталог
|- разделы и вложенность
|- назначенные документы
|- наборы характеристик
|- фильтры и условия
|- сортировка
|- шаблон карточки и листинга
`- индекс для быстрого публичного поиска
```
Рубрика отвечает за структуру одного документа. Каталог отвечает за то, где и
как множество документов показывается пользователю.
### Что хранит каталог
- собственное название и назначение;
- корневые и вложенные разделы;
- документы каждого раздела;
- используемые поля и группы полей;
- поля, включенные по умолчанию для новых разделов;
- фильтры и их порядок;
- условия показа разделов и документов;
- настройки сортировки и пагинации;
- конфигурацию карточки и листинга;
- проекцию данных для быстрого публичного чтения.
### Каталог и запросы
Обычный запрос подходит для независимой подборки: последние новости, работы
автора, акции или связанные статьи.
Каталог нужен, когда дополнительно требуются:
- постоянная иерархия разделов;
- назначение документов разделам;
- фасетные фильтры;
- согласованные карточки;
- управляемая сортировка;
- быстрый индекс большого набора документов.
Запросы и каталог дополняют друг друга. Например, главная страница может
показывать запрос «Новинки», а переход в раздел открывает полноценный каталог с
фильтрами.
### Товарные возможности
Товары являются расширением универсального каталога. Модуль товаров добавляет:
- артикул, цену, старую цену и наличие;
- товарные характеристики;
- варианты одного товара;
- карточки товара;
- товарную индексацию.
Корзина, заказы, оплаты и доставки принадлежат отдельным модулям. Их физическое
удаление не должно удалять документы, рубрики и универсальную структуру
каталога.
### Как выводится раздел каталога
1. По URL определяется каталог и его раздел.
2. Загружаются настройки раздела, набор полей и фильтров.
3. Входные значения фильтров нормализуются и проверяются.
4. Индекс выбирает подходящие документы.
5. Применяются сортировка и пагинация.
6. Для каждого документа собирается карточка.
7. Карточки объединяются в листинг.
8. Результат вставляется в публичную страницу каталога.
Карточка является отдельным управляемым шаблоном. Она не должна копироваться в
несколько системных блоков, иначе главная, каталог и подборки начинают выглядеть
и работать по-разному.
### Индексация и кеш каталога
Публичный фильтр не должен каждый раз разбирать все JSON-значения полей. После
сохранения документа его данные проецируются в индекс каталога. Индекс содержит
только значения, нужные для листинга, сортировки и фильтрации.
Перестроение требуется после изменения:
- документа или его полей;
- назначения документа разделам;
- набора характеристик;
- настроек фильтров;
- конфигурации карточки;
- структуры вариантов товара.
## Модули
Модуль — физически отделяемое расширение AVE.cms. Он может добавить:
- собственные публичные и административные маршруты;
- теги для шаблонов, рубрик и блоков;
- новые типы полей;
- hooks и события;
- таблицы и миграции;
- настройки и права;
- пункт в меню панели управления;
- действие в шапке, уведомления и Dashboard-виджет;
- фоновые web-задачи и интеграции с внешними сервисами.
### Где модуль участвует в сборке
До поиска документа `PublicKernel` загружает только включенные публичные части
установленных модулей. После этого возможны три основных сценария.
1. **Собственный маршрут.** Модуль полностью отвечает на URL и завершает запрос
без документа. Так работают API, callbacks и файловые endpoints.
2. **Страница в оболочке сайта.** Модуль предоставляет `[tag:maincontent]`, но
использует выбранный шаблон сайта, SEO и общий публичный pipeline.
3. **Встроенный компонент.** Модуль регистрирует тег, который встречается в
шаблоне сайта, рубрики, запроса или блока.
Например, форма обратной связи может зарегистрировать тег:
```text
[tag:module:contacts:feedback]
```
Шаблон отвечает только за место компонента. Валидация, отправка и хранение
данных принадлежат модулю.
### Зависимости и жизненный цикл
```text
Файлы доступны
-> установка миграций и прав
-> модуль включен
-> маршруты, меню, hooks и теги работают
```
- **Отключение** прекращает загрузку runtime-вкладов, но сохраняет данные.
- **Деинсталляция** выполняет объявленный модулем сценарий и убирает права.
- **Физическое удаление** возможно только для отделяемого пакета после
деинсталляции.
- **Зависимости** не позволяют выключить модуль, пока от него зависит другой
включенный пакет.
Ядро, рубрики и документы не должны напрямую подключать PHP-файлы модуля.
Взаимодействие строится через его публичные сервисы, теги, маршруты и hooks.
Подробнее: [Модульная система](../modules/README.md).
## Порядок сборки публичной страницы
Фактический pipeline одного публичного запроса:
1. `PublicKernel` запускает framework, сессию, авторизацию, модули, защиту и hooks.
2. Проверяются прямые публичные маршруты модулей.
3. URL разбирается на alias, пагинацию и дополнительные параметры.
4. По alias находится документ.
5. Если документа нет, проверяется история редиректов, затем загружается документ 404.
6. Вместе с документом загружаются рубрика, права и внешний шаблон сайта.
7. Проверяется полный кеш страницы.
8. Выполняется настроенный публичный код рубрики перед загрузкой документа.
9. Проверяются публикация документа и право текущей группы на чтение.
10. Основной или дополнительный шаблон рубрики заполняется полями документа.
11. Полученный HTML вставляется в `[tag:maincontent]` шаблона сайта.
12. Подставляются header, Open Graph и footer рубрики.
13. Обрабатываются поля запроса, блоки, системные блоки и тизеры.
14. Обрабатываются теги установленных модулей.
15. Выполняются сохраненные запросы.
16. Формируются навигации.
17. Подставляются SEO, данные документа, хлебные крошки и системные пути.
18. Выполняется разрешенный PHP из хранимых шаблонов.
19. Результат проходит через hooks ответа.
20. Для администратора добавляются быстрое редактирование и публичный debug.
21. Готовый HTML отправляется браузеру и при необходимости сохраняется в кеш.
При попадании в полный кеш часть внутренних шагов не выполняется повторно, но
логическая структура результата остается той же.
## Кеширование
Публичный runtime использует несколько уровней:
| Уровень | Что хранится |
| --- | --- |
| Snapshot документа | Нормализованные свойства документа и значения полей. |
| Шаблон документа | Результат заполнения шаблона рубрики. |
| Полная страница | Шаблон сайта вместе с содержимым и компонентами. |
| Запрос | Результат тяжелой выборки или листинга. |
| Каталог | Проекция для фильтрации, сортировки и карточек. |
Ключи учитывают документ, рубрику, шаблон, группу пользователя, версии полей и
пагинацию. После сохранения документа, рубрики, поля, запроса, шаблона или
конфигурации каталога связанные записи должны инвалидироваться.
Подробнее: [Кеш базы и контента](../database/cache.md).
## Hooks публичного pipeline
Hook — именованная точка вмешательства в работу системы. Модуль подписывает
обработчик на событие и получает структурированный контекст. Это позволяет
добавлять проектную логику без изменения ядра.
### Основные публичные точки
```text
frontend.request.received
frontend.route.resolved
content.document.loading
content.document.loaded
content.rubric.loading
content.rubric.loaded
content.query.loading
content.query.loaded
content.query.rendering
content.query.rendered
content.template.rendering
content.template.rendered
frontend.response.rendering
frontend.response.rendered
```
События `loading` позволяют подготовить или заменить источник до чтения.
События `loaded` получают уже загруженный объект и могут дополнить результат.
События `rendering` и `rendered` работают с содержимым до и после рендера.
### Сохранение документов
Для административного и API-сохранения предусмотрены отдельные стадии:
```text
content.document.saving
content.document.saved
content.document.created
content.document.updated
content.document.published
content.document.unpublished
content.document.deleting
content.document.deleted
content.document.snapshot_built
```
Точные названия доступных событий и их контексты показываются в каталоге hooks.
Обработчик должен проверять рубрику, ID или состояние документа и быстро
завершаться, если событие ему не подходит.
Примеры применения:
- опубликовать новую статью в Telegram или Дзен;
- передать заказ во внешнюю CRM;
- проверить сертификат товара перед публикацией;
- дополнить документ данными внешнего API;
- поставить задачу на перестроение поискового индекса;
- очистить CDN или сторонний кеш после изменения страницы.
### Hooks полей
Тип поля сам отвечает за нормализацию и валидацию значения, но модуль может
подписаться на общий цикл поля:
```text
content.field.normalizing
content.field.normalized
content.field.validating
content.field.validated
content.field.saving
content.field.saved
content.field.rendering
content.field.rendered
```
Обработчик обязательно ограничивается alias, ID или типом поля. Иначе он будет
запускаться для каждого поля каждого документа.
### Каталог и hooks
Индекс каталога обновляется вслед за сохранением документа и его snapshot,
поэтому интеграции могут использовать фактические события
`content.document.saved` и `content.document.snapshot_built`. Инвалидация
проходит через `cache.invalidating` и `cache.invalidated`.
Устанавливаемый каталоговый или товарный модуль может объявить дополнительные
события через `hook_definitions`. Их имена и контекст становятся частью
контракта именно этого модуля и появляются в общем каталоге hooks. Использовать
необъявленное имя как системный контракт нельзя.
### Изменение и отмена результата
`LifecycleEvent` может вернуть измененный результат. Отмена поддерживается
только на стадиях, где это явно заявлено контрактом. При сохранении документа
для контролируемого отказа используется `DocumentSaveEvent::fail()`. Ошибка
должна быть понятной и не оставлять частично сохраненные данные.
Для побочных действий после сохранения предпочтительна стадия `saved`. Для
валидации и запрета операции используется стадия до записи. Внешний HTTP-вызов
не следует выполнять внутри длинной транзакции, если его можно отложить до
успешного сохранения.
Полный каталог, приоритеты, форматы контекста и отладка:
[Хуки и события](../hooks/README.md).
## Практический порядок создания сайта
1. Создать внешний шаблон сайта с `[tag:maincontent]`.
2. Создать рубрики для реальных типов контента.
3. Добавить и расположить поля каждой рубрики.
4. Собрать основной шаблон рубрики и необходимые альтернативы.
5. Создать документы и заполнить значения полей.
6. Настроить alias и проверить историю редиректов.
7. Создать навигации и привязать документы или URL.
8. Создать запросы для лент и связанных материалов.
9. Вынести повторяемые части в блоки.
10. При необходимости создать каталог, его разделы, поля и фильтры.
11. Установить только нужные проекту модули.
12. Проверить права, SEO, 404, пагинацию, кеш и мобильный вывод.
## Типовые композиции
### Новостной сайт
```text
Рубрики: Новости, Статьи, Страницы
Запросы: Последние, Популярные, По теме
Каталог: необязателен; может использоваться как рубрикатор публикаций
Модули: RSS, формы, авторизация — по необходимости
```
### База знаний
```text
Рубрики: Инструкции, FAQ, Справочные страницы
Каталог: разделы знаний и фильтры по продукту/версии
Запросы: Обновленные, Связанные, Популярные
Навигация: основное меню и дерево документации
```
### Интернет-магазин
```text
Рубрики: Товары, Страницы, Новости
Каталог: товарные разделы, характеристики и фильтры
Модуль товаров: цены, наличие, варианты и карточки
Commerce: корзина, заказы, избранное и история просмотров
Gateways: оплаты, авторизация и доставки отдельными пакетами
```
## Частые ошибки
- Размещать всю разметку документа во внешнем шаблоне сайта.
- Использовать меню для формирования URL документа.
- Копировать одну карточку в несколько системных блоков.
- Использовать длинную цепочку `И/ИЛИ` без групп условий.
- Делать каталог зависимым от товаров и корзины.
- Читать JSON всех полей при каждом публичном фильтре вместо индекса каталога.
- Помещать большую бизнес-логику в хранимый шаблон вместо сервиса или модуля.
- Забывать инвалидировать кеш после изменения шаблона, поля или каталога.