Перейти к содержимому

Биндинги и слоты

Обычный блок показывает то, что ввёл контентщик. Блок с привязками показывает живые данные витрины: товары рубрики, содержимое корзины, избранное. Механизм состоит из двух половин:

  • слот (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, страница не падает, в редакторе появляется предупреждение

Привязки не берутся из базы «как есть» — набор пересобирается по актуальной схеме блока. Для каждого слота:

  1. есть сохранённая привязка с непустым source → берётся она;
  2. иначе есть default → берётся он, params пустые;
  3. иначе берётся первый элемент allowedSources;
  4. если нет ни того, ни другого — слот остаётся незаполненным;
  5. если выбранный источник контекстный, а документ нужного окружения не даёт — слот тоже остаётся незаполненным (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).