Биндинги и слоты
Обычный блок показывает то, что ввёл контентщик. Блок с привязками показывает живые данные витрины: товары рубрики, содержимое корзины, избранное. Механизм состоит из двух половин:
- слот (
bindingSlotsв схеме блока) — потребность: «мне нужен список товаров, допустимы такие-то источники»; - привязка (
bindingsу вхождения блока на странице) — фактический выбор: «этот источник с этими параметрами».
Данные слота приходят в шаблон под именем слота в скоупе data.
Объявление слота
Заголовок раздела «Объявление слота»bindingSlots — объект рядом с fields в корне схемы блока. Ключ объекта
становится именем переменной в шаблоне.
{ "type": "object", "label": "Карточки товаров", "fields": { "title": { "type": "text", "label": "Заголовок секции", "default": "Хиты продаж" } }, "bindingSlots": { "products": { "resultType": "product_list", "allowedSources": ["catalog.products", "catalog.favorits"], "default": "catalog.products" } }}| Ключ | Обязателен | Что задаёт |
|---|---|---|
resultType |
да | форму данных — какой тип результата придёт в слот |
allowedSources |
да, минимум один | из каких источников контентщику разрешено выбирать |
default |
нет | источник, выбранный для нового вхождения блока |
Слотов может быть несколько — каждый со своим типом результата и списком источников. Посторонние ключи внутри слота схема не примет.
Что проверяется при сохранении блока
Заголовок раздела «Что проверяется при сохранении блока»- каждый источник из
allowedSourcesсуществует в реестре; - у каждого источника
result_type_idсовпадает с объявленнымresultType; defaultвходит вallowedSources;- если реестр вообще пуст — сохранение отклоняется с просьбой синхронизировать его.
Ошибка в слоте — ошибка автора блока, поэтому ловится она при сохранении блока, а не при публикации страницы месяцем позже.
Привязка на странице
Заголовок раздела «Привязка на странице»Ключ — имя слота, значение — { source, params }:
{ "block_id": 12, "placeholder_values": { "title": "Хиты продаж" }, "bindings": { "products": { "source": "catalog.products", "params": { "limit": 8, "sort": "popular" } } }}Правила:
sourceобязателен и должен входить вallowedSourcesслота — либо быть контекстным источником, подходящим этому документу;params— объект; его форму задаётparams_schemaвыбранного источника (см. Реестр источников);- имя слота должно существовать в схеме — иначе 400 с перечислением доступных слотов;
bindingsу блока безbindingSlots— тоже ошибка, а не безобидное лишнее поле;- пустой
{}равнозначен «не передано».
bindings задаются вместе с блоком: POST /api/v1/pages, POST /api/v1/pages/{id}/blocks,
PUT /api/v1/pages/{id}. Точечная правка PUT /api/v1/pages/{id}/blocks/{pageBlockId}
меняет только placeholder_values; чтобы сменить источник, переприкрепите блок.
Контекстные источники: данные текущей рубрики или товара
Заголовок раздела «Контекстные источники: данные текущей рубрики или товара»Часть источников берёт данные не по параметрам, а из того, что сейчас показывают
на витрине: rubrics.current отдаёт текущую рубрику, catalog.current_products —
товары текущего листинга. У такого источника непустое поле context — список
окружений, в которых он работает:
{ "id": "catalog.current_products", "result_type_id": "product_list", "context": ["rubric", "brand", "search"]}Привязать контекстный источник можно только к документу, чей вид (kind) даёт одно
из этих окружений. Окружения вида документа перечислены в contexts ответа
GET /api/v1/page-types: например у rubric это ["rubric","brand","search"],
у product — ["product","rubric","brand"]. Страница (article) окружений не даёт
вовсе.
В allowedSources контекстные источники не перечисляются. Автор блока не знает,
на документ какого вида поставят блок, поэтому в схеме указывается только resultType
слота, а редактор предлагает подходящие контекстные источники сам. Так один и тот же
блок работает и на шаблоне рубрики (данные из контекста), и на лендинге (данные
из явного источника) — без правок схемы.
Что происходит при несовпадении:
| Ситуация | Поведение |
|---|---|
| привязка контекстного источника к документу без нужного окружения | ошибка валидации payload с причиной |
у документа kind = layout |
проверки нет: куда назначат макет, знает 4CMS, поэтому предлагаются все контекстные источники |
| окружение исчезло у вида документа уже после сохранения | слот приходит в шаблон как null, страница не падает, в редакторе появляется предупреждение |
Дефолты подставляются сами
Заголовок раздела «Дефолты подставляются сами»Привязки не берутся из базы «как есть» — набор пересобирается по актуальной схеме блока. Для каждого слота:
- есть сохранённая привязка с непустым
source→ берётся она; - иначе есть
default→ берётся он,paramsпустые; - иначе берётся первый элемент
allowedSources; - если нет ни того, ни другого — слот остаётся незаполненным;
- если выбранный источник контекстный, а документ нужного окружения не даёт —
слот тоже остаётся незаполненным (
data.<слот>будетnull).
Поэтому bindings можно вовсе не передавать: минимально рабочая конфигурация
соберётся сама. Это же спасает при обновлении блока: автор добавил новый слот
минорной версией — страницы, ничего не знающие о новом слоте, всё равно уедут
на витрину с заполненной привязкой.
Обратная сторона: фактическая привязка может отличаться от того, что лежит в базе,
а порядок элементов в allowedSources значим — первый работает запасным дефолтом.
Данные в шаблоне
Заголовок раздела «Данные в шаблоне»Имя слота становится ключом в data:
<h2>{{ props.title }}</h2><ul> {% for product in data.products.items %} <li> <a href="{{ product.url }}"> <img src="{{ product.variation.images.imgUrl }}" alt="{{ product.name }}" loading="lazy"> <span>{{ product.name }}</span> <b>{{ product.variation.price.formatted }}</b> </a> </li> {% endfor %}</ul>
{% if data.products.total > data.products.items.size %} <p>Всего товаров: {{ data.products.total }}</p>{% endif %}Конкретный набор полей внутри data.products задаётся не блоком, а типом
результата: его schema_json описывает структуру, а stub показывает пример
тех же данных. Оба доступны через API — см. Реестр источников.
Liquid работает в нестрогом режиме: обращение к несуществующему полю даёт пустую строку, а не ошибку. Удобно при отладке и опасно при опечатках.
Предпросмотр идёт на заглушке
Заголовок раздела «Предпросмотр идёт на заглушке»В редакторе данных витрины нет, поэтому в data.<слот> подставляется stub типа
результата. Блок сразу выглядит правдоподобно, но:
Если у типа результата заглушки нет, слот в предпросмотре получит null.
Ограничения
Заголовок раздела «Ограничения»- Параметры привязки не валидируются против
params_schema. - Источник, помеченный устаревшим или исчезнувший из реестра, продолжает работать в существующих привязках без предупреждения. Предупреждение появляется только у контекстного источника, чьё окружение пропало у вида документа.
- У документа
kind = layoutсовместимость контекстного источника не проверяется — пустой слот обнаружится на витрине. - Запасной выбор «первый из
allowedSources» делает порядок списка значимым. - Предпросмотр на заглушке не гарантирует работоспособность на реальных данных.
Источники: backend/src/Service/Block/BlockBindingsResolver.php,
backend/src/Service/Block/BindingContextChecker.php,
backend/src/Service/Block/BlockPlaceholderValidator.php,
backend/src/Service/BlockSchemaValidator.php (validateBindingSlots),
backend/src/Service/Page/PageBlockService.php,
backend/public/block-schema.json (bindingSlots),
backend/src/OpenApi/Schemas/PageBlockInput.php,
front/src/lib/placeholder.ts (assembleBlockHtml).