Метабоксы WordPress: создание и вывод произвольных полей
← Блог
WordPress · 05.10.2026 · 7 мин чтения

Метабоксы WordPress: создание и вывод произвольных полей

Произвольные поля WordPress хранят данные записи: цену, подзаголовок или ссылку на источник. Метабокс — форма в редакторе, через которую эти данные заполняют.

Создадим блок «Источник» с названием и ссылкой: зарегистрируем поля, настроим сохранение и выведем их в шаблоне. Затем разберём доступ через REST API и альтернативный вариант на Carbon Fields.

Как хранятся произвольные поля

Поля лежат в таблице wp_postmeta: номер записи, ключ и значение. Работают с ними три функции:

  • get_post_meta( $post_id, $key, true ) — прочитать одно значение. С третьим аргументом false вернётся массив всех значений с этим ключом.
  • update_post_meta( $post_id, $key, $value ) — записать значение. Если ключа нет, он будет создан.
  • delete_post_meta( $post_id, $key ) — удалить.

Ключи, которые начинаются с подчёркивания, WordPress считает служебными и не показывает в стандартной панели «Произвольные поля». Для полей с собственным метабоксом это то, что нужно: иначе одно значение окажется в редакторе дважды.

1. Создание метабокса через add_meta_box()

Пример — блок «Источник» с названием и ссылкой в боковой колонке редактора записи. Сначала регистрация и разметка формы в functions.php:

function mytheme_add_source_metabox() {
    add_meta_box(
        'mytheme_source',                // ID блока
        'Источник',                      // заголовок
        'mytheme_render_source_metabox', // функция вывода
        'post',                          // тип записи
        'side'                           // колонка: normal, side или advanced
    );
}
add_action( 'add_meta_boxes', 'mytheme_add_source_metabox' );

function mytheme_render_source_metabox( $post ) {
    $title = get_post_meta( $post->ID, '_mytheme_source_title', true );
    $url   = get_post_meta( $post->ID, '_mytheme_source_url', true );

    wp_nonce_field( 'mytheme_save_source', 'mytheme_source_nonce' );
    ?>
    <p>
        <label for="mytheme-source-title">Название</label>
        <input type="text" id="mytheme-source-title" name="mytheme_source_title"
            value="<?php echo esc_attr( $title ); ?>" class="widefat">
    </p>
    <p>
        <label for="mytheme-source-url">Ссылка</label>
        <input type="url" id="mytheme-source-url" name="mytheme_source_url"
            value="<?php echo esc_attr( $url ); ?>" class="widefat">
    </p>
    <?php
}

wp_nonce_field() добавляет скрытый токен для проверки запроса. В WordPress nonce не является строго одноразовым: он действует ограниченное время и помогает защитить форму от CSRF. Право пользователя менять запись проверяется отдельно.

Значения в HTML-атрибутах экранируются через esc_attr(), чтобы кавычки и другие символы не повредили разметку.

2. Сохранение полей: nonce, права и очистка

function mytheme_save_source_metabox( $post_id ) {
    $nonce = isset( $_POST['mytheme_source_nonce'] )
        ? sanitize_text_field( wp_unslash( $_POST['mytheme_source_nonce'] ) )
        : '';

    if ( ! wp_verify_nonce( $nonce, 'mytheme_save_source' ) ) {
        return;
    }
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    $fields = array(
        '_mytheme_source_title' => sanitize_text_field( wp_unslash( $_POST['mytheme_source_title'] ?? '' ) ),
        '_mytheme_source_url'   => esc_url_raw( wp_unslash( $_POST['mytheme_source_url'] ?? '' ) ),
    );

    foreach ( $fields as $key => $value ) {
        if ( '' === $value ) {
            delete_post_meta( $post_id, $key );
        } else {
            update_post_meta( $post_id, $key, $value );
        }
    }
}
add_action( 'save_post_post', 'mytheme_save_source_metabox' );
  • Хук save_post_post срабатывает только для записей типа post. Для страниц нужен save_post_page, для своего типа — save_post_{тип}. Общий save_post вызывается и для ревизий, и для всех типов сразу.
  • Проверка nonce помогает отклонить поддельную отправку формы. Проверка DOING_AUTOSAVE пропускает автосохранение: оно не должно затирать значения метабокса.
  • current_user_can( 'edit_post', $post_id ) проверяет право редактировать именно эту запись, а не записи вообще.
  • Данные из $_POST приходят с экранированными кавычками: wp_unslash() снимает экранирование, затем значение очищается по типу. Текст — через sanitize_text_field(), ссылка — через esc_url_raw(), число — через absint().
  • Пустое поле удаляет ключ, чтобы в базе не копились пустые строки.

В блочном редакторе такие метабоксы работают: блок с 'side' появится в боковой панели записи, остальные — под текстом. При нажатии «Сохранить» редактор отдельным запросом отправляет форму метабоксов, и хук срабатывает как в классическом редакторе.

3. Вывод произвольных полей в шаблоне

В single.php внутри цикла, например после текста записи:

<?php
$source_title = get_post_meta( get_the_ID(), '_mytheme_source_title', true );
$source_url   = get_post_meta( get_the_ID(), '_mytheme_source_url', true );

if ( $source_url ) :
    $label = $source_title ? $source_title : wp_parse_url( $source_url, PHP_URL_HOST );
    ?>
    <p class="post-source">
        Источник: <a href="<?php echo esc_url( $source_url ); ?>" rel="nofollow"><?php echo esc_html( $label ); ?></a>
    </p>
<?php endif; ?>

Значения экранируются ещё раз при выводе: esc_url() для адреса, esc_html() для текста. Очистка при сохранении не отменяет экранирования в шаблоне — поле могло попасть в базу другим путём, например через импорт.

Регистрация поля для REST API

Если поле нужно в REST API или в собственных блоках редактора, его регистрируют через register_post_meta(). Так WordPress знает тип поля, функцию очистки и кто может его менять:

function mytheme_register_source_meta() {
    register_post_meta( 'post', '_mytheme_source_url', array(
        'type'              => 'string',
        'single'            => true,
        'sanitize_callback' => 'esc_url_raw',
        'show_in_rest'      => true,
        'auth_callback'     => function ( $allowed, $meta_key, $post_id ) {
            return current_user_can( 'edit_post', $post_id );
        },
    ) );
}
add_action( 'init', 'mytheme_register_source_meta' );

Зарегистрированное поле доступно в объекте meta REST API, если тип записи поддерживает custom-fields. Не включай show_in_rest для секретных данных: регистрация поля может сделать его значение доступным при чтении записи.

В примере auth_callback задаёт проверку права изменять защищённое поле с подчёркиванием. Функция очистки из register_post_meta() применяется и при вызове update_post_meta().

Метабокс на Carbon Fields

Carbon Fields — библиотека, которая строит метабоксы из описания полей. Форму, nonce и сохранение она берёт на себя. Удобна, когда полей много или нужны повторяющиеся группы, картинки и условия показа. Подключается плагином или через Composer.

<?php
use Carbon_Fields\Container;
use Carbon_Fields\Field;

function mytheme_source_fields() {
    Container::make( 'post_meta', 'Источник' )
        ->where( 'post_type', '=', 'post' )
        ->set_context( 'side' )
        ->add_fields( array(
            Field::make( 'text', 'mytheme_source_title', 'Название' ),
            Field::make( 'text', 'mytheme_source_url', 'Ссылка' )
                ->set_attribute( 'type', 'url' ),
        ) );
}
add_action( 'carbon_fields_register_fields', 'mytheme_source_fields' );
  • Строки use пишутся в начале PHP-файла, вне функций. Удобно вынести поля в отдельный файл, например inc/carbon-fields.php, и подключить его из functions.php.
  • Если библиотека установлена через Composer, а не плагином, её нужно запустить: \Carbon_Fields\Carbon_Fields::boot(); на хуке after_setup_theme.
  • Условия where() ограничивают показ: по типу записи, шаблону страницы (post_template), рубрике. Их можно комбинировать.

Читать значение лучше функцией библиотеки:

<?php
$source_url = carbon_get_post_meta( get_the_ID(), 'mytheme_source_url' );

Для чтения полей Carbon Fields используй carbon_get_post_meta(): так код не зависит от внутреннего способа хранения. Простые значения можно использовать в запросах к wp_postmeta, проверив фактическое имя ключа.

Составные поля complex читай через API библиотеки. Экранирование при выводе остаётся в шаблоне: атрибут type="url" у поля ввода не заменяет проверку данных на сервере.

Когда использовать API WordPress или библиотеку

  • Одно-два простых поля — стандартный add_meta_box. Нет зависимостей, всё под контролем.
  • Много полей, повторяющиеся блоки, галереи, страницы настроек — Carbon Fields или ACF. Меньше кода и единый интерфейс.
  • Картинку записи не нужно хранить в произвольном поле: для этого есть миниатюры записей.

Документация

Есть вопрос или дополнение?

Обсудить в Telegram

Добавить комментарий