Search by

besnovatyj / yii2-cms-blocks

besnovatyj

Модуль блоков Yii2 CMS: управляемое из админки содержимое мест, объявленных темой

Package info

github.com/besnovatyj/yii2-cms-blocks

Type:yii2-extension

pkg:composer/besnovatyj/yii2-cms-blocks

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.1.3 2026-10-19 15:51 UTC

This package is auto-updated.

Last update: 2026-10-19 15:54:38 UTC


README

Модуль блоков — управляемое из админки содержимое мест, которые объявляет тема (аналог widget areas в WordPress).

Зачем

Тема не может заводить свои CRUD в админке, а вещей вида «объявление над шапкой», «всплывающее окно», «текст в колонке подвала» у неё много. Тема объявляет места, модуль хранит их начинку.

Как это устроено

Роль Кто Что делает
Место тема объявляет его в areas.php и вызывает BlockArea::widget(['area' => '…']) в разметке
Каркас тема обёртка, кнопки, id для скриптов, всё, что не должно ломаться правкой текста
Блок этот модуль начинка места: данные по схеме типа, статус, окно показа, порядок
Тип блока модуль, тема или другой модуль схема полей и шаблон вывода, см. «Типы блоков»
Заготовка тема → пикер сниппетов разметка начинки в стиле темы, с которой удобно начать
  • Каталог мест приходит через контракт Besnovatyj\Contracts\theme\ThemeAreaCatalog, реализация — в пакете тем (yii2-cms-themes). Пакета тем нет: место задаётся в блоке вручную.
  • Блоки в месте выводятся подряд по sort_order; выключенные и вне окна показа пропускаются. Блок в месте, которое активная тема не объявляет, хранится, но не выводится (админка это показывает).
  • Кэш: все включённые блоки — один элемент кэша на все места с тегом blocks, сбрасывается при любой правке. Окно показа проверяется на каждый запрос, поэтому наступившая дата не ждёт сброса.
  • Шорткоды: текстовые (%name%) разворачиваются при выводе в HTML-полях, если подключён модуль шорткодов.

Типы блоков

Блок — это данные по схеме его типа, разметку рисует шаблон типа. Менеджер заполняет форму, а не верстает.

Источник Id Где объявлен
Встроенные html, embed src/types/BuiltInTypes.php, шаблоны в src/views/blockTypes/
Тема cards-3 (без префикса) themes/{тема}/blockTypes/{type}/schema.php + view.php
Модуль Performance:showcase класс модуля реализует Besnovatyj\Contracts\block\BlockTypeProvider
  • Встроенные типы разрешены в любом месте. Остальные место перечисляет в areas.php ключом types.
  • Тип задаётся при создании и не меняется.
  • embed — код чужого виджета, выводится как есть. Создать такой блок и править его код можно только с разрешением /Blocks/backend/embed/edit (псевдомаршрут RBAC, контроллёра за ним нет; его дают и шаблоны /Blocks/backend/*, /Blocks/*). В админке код только показывается текстом.
  • Шаблон модульного или встроенного типа тема перекрывает обычным overlay: themes/{тема}/modules/{ModuleId}/views/blockTypes/…. Шаблон рендерится через View::renderFile().
  • Тип пропал (сменили тему, удалили модуль) — блок не выводится, данные хранятся, админка помечает.

Схема

// themes/berdrama/blockTypes/cards-3/schema.php
return [
    'label' => 'Три карточки в ряд',
    'hint'  => 'Изображение, подпись и ссылка у каждой карточки.',
    'fields' => [
        'heading' => ['kind' => 'text', 'label' => 'Заголовок раздела', 'maxLength' => 120],
        'cards'   => ['kind' => 'repeater', 'label' => 'Карточки', 'min' => 1, 'max' => 3, 'fields' => [
            'image'   => ['kind' => 'image', 'label' => 'Изображение', 'required' => true],
            'caption' => ['kind' => 'text', 'label' => 'Подпись'],
            'link'    => ['kind' => 'link', 'label' => 'Ссылка'],
        ]],
    ],
];

Ключи поля: kind (обязателен), label, hint, required, default, maxLength (text, textarea), options (select, обязателен), min/max (repeater), fields (repeater).

kind Значение в $data Замечание
text, textarea строка
richtext HTML Jodit со сниппетами; выводить через $block->html() (текстовые шорткоды)
image ['file' => …, 'alt' => …] файл — относительно папки блока; выводить через $block->imageUrl()
link ['url' => …, 'route' => …, 'params' => […], 'blank' => bool] раздел из MenuTargetProvider или свой URL; выводить через $block->linkUrl() и $block->linkAttributes() (флажок «в новой вкладке» → target="_blank" rel="nofollow noopener")
select ключ из options options — массив или Closure (ленивый список из БД модуля, см. ниже)
checkbox bool
repeater список строк без вложенных repeater; richtext и code внутри строки недоступны
code строка только для кода виджетов, требует разрешения embed

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

Свободный URL ссылки принимается только относительный, #, http(s)://, mailto:, tel:.

Ленивый список select ('options' => fn () => $repo->options()) — для вариантов из БД модуля: витрины, рубрики, галереи. Замыкание вызывают только форма и проверка при сохранении; при выводе блока значение берётся как сохранено, без запроса. Вариант, пропавший после сохранения (удалили рубрику), шаблон или поставщик данных обрабатывают сами — обычно ничего не находят и блок не выводят.

Шаблон

<?php
use Besnovatyj\Blocks\render\BlockViewContext;
use yii\helpers\Html;

/** @var array $data данные по схеме */
/** @var BlockViewContext $block id, тип, место и помощники вывода */
?>
<section class="cards-3">
    <?php if ($data['heading'] !== ''): ?><h2><?= Html::encode($data['heading']) ?></h2><?php endif; ?>
    <?php foreach ($data['cards'] as $card): ?>
        <a href="<?= Html::encode($block->linkUrl($card['link'])) ?>"<?= $block->linkAttributes($card['link']) ?>>
            <img src="<?= Html::encode($block->imageUrl($card['image'])) ?>" alt="<?= Html::encode($card['image']['alt']) ?>">
            <?= Html::encode($card['caption']) ?>
        </a>
    <?php endforeach; ?>
</section>

Каркас вокруг каждого блока не навязывается: шаблон сам решает, нужна ли ему обёртка. Ошибка шаблона в режиме отладки пробрасывается, на проде блок пропускается с записью в лог.

Типы модулей

Модуль реализует BlockTypeProvider::blockTypes() и возвращает BlockType с локальным id (showcase), префикс {moduleId}: ставит модуль блоков. Данные, которых нет в полях блока (спектакли, посты), отдаёт класс BlockType::$dataProvider (BlockDataProvider::provide(array $data): array), созданный DI-контейнером. Кэширует выборку сам поставщик: готовый HTML блоков модуль блоков не кэширует.

Файлы блока

У каждого блока своя папка @static/origin/Blocks/{id}: создаётся вместе с блоком (у старых блоков — при первом открытии формы), удаляется вместе с ним. Редактор и выбор изображений в форме блока заперты в этой папке. Поэтому содержимое заполняется после первого сохранения.

Блоки ≠ шорткоды ≠ сниппеты

  • Шорткод — плейсхолдер в контенте, разворачивается в код при рендере (контент → код).
  • Место — «дырка» в коде темы, которую заполняет контент из админки (код → контент).
  • Сниппет — заготовка разметки, вставляемая в редактор при правке.

Механизмы компонуются: блок набирают со сниппета темы, внутри могут быть текстовые шорткоды.

Вывод в теме

use Besnovatyj\Blocks\widgets\BlockArea;

<?php $content = BlockArea::widget(['area' => 'popup-bar']); ?>
<?php if ($content !== ''): ?>
    <div class="…"><?= $content ?></div>
<?php endif; ?>

inline => true снимает с начинки обёртку <p>, если блок типа html — один абзац без атрибутов: для мест, которые встают в строку (копирайт, заголовок).

Объявление мест в теме

themes/{тема}/areas.php:

return [
    'popup-bar' => ['label' => 'Полоса над шапкой', 'hint' => 'Текст объявления и кнопка-ссылка', 'sort' => 10],
    'home'      => ['label' => 'Главная', 'types' => ['cards-3', 'Performance:showcase'], 'sort' => 5],
];

types — типы сверх встроенных (html, embed), которые место принимает. Не задан — только встроенные.

Установка

modman install Blocks + recompile (+ reload php-fpm на проде).