Шорткоды и Gutenberg-блоки своими руками: выбор без переделок
Шорткод в базе — строка, статический блок — готовый HTML, динамический — только атрибуты. От этого зависит, что увидит посетитель после отключения плагина и что случится при первой правке кода. Разбираем на плашке-уведомлении, WordPress 6.x.

Сайт сменил тему, и на каждой странице, где стояла плашка-уведомление, вместо неё торчит сырой текст [mp_notice type="warning"]. Шорткод жил в functions.php старой темы. Обратная история: вы поправили одну строку в save() блока, и старые плашки в редакторе показывают предупреждение о недопустимом содержимом. Корень один: шорткод и блок хранятся в записи по-разному, а туториалы редко говорят, чем это грозит. Разберём три подхода на одной задаче: плашка с типом info или warning и текстом. Версии: WordPress 6.3 и новее, PHP 8.x.
Три подхода и их цена
Вопрос не в том, что проще написать. Вопрос в том, что останется в базе и что будет, когда код изменится или исчезнет.
| Подход | Что лежит в записи | Выигрыш | Цена | Если отключить плагин |
|---|---|---|---|---|
| Шорткод | Текст вида [mp_notice ...] | Работает в виджетах и шаблонах через do_shortcode | Нет предпросмотра, атрибуты вводят руками | На странице виден сырой шорткод |
| Статический блок | Готовый HTML и комментарий с атрибутами | PHP при показе не нужен | Любая правка save() без deprecated ломает старые блоки | HTML остаётся, стили и скрипты пропадают |
| Динамический блок | Только комментарий с атрибутами | Вёрстку можно менять без миграций | PHP работает на каждом показе, нужна сборка JS | Блок не выводится совсем |
Шорткод — это строка, которую кто-то должен понять. Блок — договор между разметкой в базе и кодом в плагине. Нарушите договор, и платить придётся за каждую старую запись.
Шорткод: четыре правила
Код живёт в плагине, а не в functions.php: смена темы не должна ломать контент. Одна функция рендера обслуживает и шорткод, и блок, поэтому её стоит выделить сразу.
<?php
/**
* Plugin Name: MP Notice
* Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Одна функция на два входа: шорткод и блок.
function mp_notice_render( $type, $text ) {
// Значение из атрибута проверяем по белому списку.
if ( ! in_array( $type, array( 'info', 'warning' ), true ) ) {
$type = 'info';
}
return sprintf(
'<div class="mp-notice mp-notice--%s">%s</div>',
esc_attr( $type ),
wp_kses_post( $text )
);
}
function mp_notice_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts( array( 'type' => 'info' ), $atts, 'mp_notice' );
return mp_notice_render( $atts['type'], do_shortcode( $content ) );
}
add_shortcode( 'mp_notice', 'mp_notice_shortcode' );
- Колбэк возвращает строку. Если сделать
echo, плашка уедет в начало записи, выше остального текста. - Тег с префиксом. Повторная регистрация того же тега молча перезапишет чужой шорткод.
- Имена атрибутов парсер приводит к нижнему регистру. Булев атрибут приходит строкой
"false", а она в PHP истинна. - Для вложенных шорткодов вызывайте
do_shortcodeна содержимом, как выше.
Динамический блок: минимум рабочего кода
Каркас проще взять у npx @wordpress/create-block. У шаблона есть динамический вариант, флаги смотрите в --help вашей версии пакета. Дальше три файла.
Файл block.json. Поле render работает с WordPress 6.1. На версии 6.3 появился apiVersion 3, на более старых ставьте 2.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "mp/notice",
"title": "Плашка",
"category": "text",
"attributes": {
"type": { "type": "string", "default": "info" },
"text": { "type": "string", "default": "" }
},
"editorScript": "file:./index.js",
"render": "file:./render.php"
}
Файл index.js. Редактор рисует предпросмотр серверным кодом, поэтому вёрстка живёт в одном месте. save возвращает null: в базу HTML не пишется.
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl, TextControl } from '@wordpress/components';
import ServerSideRender from '@wordpress/server-side-render';
import metadata from './block.json';
registerBlockType( metadata.name, {
edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody title='Плашка'>
<SelectControl
label='Тип'
value={ attributes.type }
options={ [
{ label: 'Информация', value: 'info' },
{ label: 'Предупреждение', value: 'warning' },
] }
onChange={ ( type ) => setAttributes( { type } ) }
/>
<TextControl
label='Текст'
value={ attributes.text }
onChange={ ( text ) => setAttributes( { text } ) }
/>
</PanelBody>
</InspectorControls>
<ServerSideRender block={ metadata.name } attributes={ attributes } />
</div>
);
},
save: () => null,
} );
Файл render.php и регистрация в главном файле плагина:
<?php
// render.php. Доступны $attributes, $content и $block.
// Функция сама экранирует значения.
echo mp_notice_render( $attributes['type'] ?? 'info', $attributes['text'] ?? '' );
// Главный файл плагина.
add_action( 'init', function () {
register_block_type( __DIR__ . '/build' );
} );
Пояснение. Динамический блок не хранит HTML в записи. В базе лежит только комментарий-разделитель с атрибутами, а разметку PHP собирает при каждом показе страницы.
Статический блок: когда оправдан
Когда оформление должно пережить удаление плагина и логики в нём нет. Платите вы при каждой правке save(): старая версия остаётся в массиве deprecated со своими атрибутами и своим save. Без этого редактор не узнаёт разметку и предлагает восстановить блок. Если вы не готовы вести этот массив годами, берите динамический вариант.
Когда что выбирать
- Вывод нужен в виджетах, шаблонах или у редакторов на классическом редакторе: шорткод.
- Вывод зависит от данных (цена, остаток, выборка из базы): динамический блок.
- Чистое оформление без логики, которое должно пережить плагин: статический блок.
- Контент уже на шорткодах, нужно уйти на блоки: оставьте общую функцию рендера, добавьте блок, старые записи не трогайте. Автозамену шорткода на блок даёт
transformsс типомshortcode, синтаксис смотрите в документации блочного редактора.
Что ломается
- Блок не появляется в редакторе. Чаще всего
register_block_typeсмотрит вsrcвместоbuildили в имени нет пространства имён (нужноmp/notice). - Настройки из
supports(цвет, отступы) не действуют. В своей обёртке нуженget_block_wrapper_attributes(), иначе их классы и стили не попадут в разметку. - Тяжёлый запрос в
render.phpвыполняется на каждом показе. Без кеша страницы это прямой удар по времени ответа. - Стили шорткода подключают через
wp_enqueue_styleвнутри колбэка. В классических темах такой стиль может уйти в подвал страницы. - Атрибут блока без проверки в рендере. Значение лежит в базе, и редактировать его можно в обход интерфейса.
Что делать дальше
Включите WP_DEBUG на тестовой копии и откройте консоль браузера в редакторе: ошибки регистрации и валидации видны там первыми. Затем поставьте Query Monitor и замерьте число запросов и время страницы с блоком и без него. Если цифры заметны, добавьте кеш в рендер. Кеширование и переводы строк — отдельные темы, здесь они не разбираются.
Дефектный акт по фото: Осмотр по фотографиям: классификация дефектов, ссылки на нормы, требования к устранению. Готовый акт в Word.
Спросить за 80 ₽Источники: WordPress Developer Resources: Block metadata (block.json), WordPress Developer Resources: add_shortcode()
Частые вопросы
Что будет с шорткодом, если отключить плагин?
Можно ли вставить мой шорткод в редактор блоков?
У меня WordPress старше 6.1 или 6.3. Что менять?
Редактор пишет, что блок содержит недопустимое содержимое. Что делать?
Не сработало: блока нет в списке вставки. С чего начать?
Что учесть при выкладке на боевой сайт?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.