Произвольные поля
У книги есть цена и число страниц, у события — дата и место, у вакансии — зарплата. Такие данные не помещаются в заголовок и текст. WordPress хранит их в метаданных — парах «ключ — значение», привязанных к записи.
Чтение и запись
update_post_meta($postId, 'book_price', 450);
$price = get_post_meta($postId, 'book_price', true);
delete_post_meta($postId, 'book_price');Третий аргумент get_post_meta() — true — возвращает одно значение. Без него функция вернёт массив: под одним ключом может храниться несколько значений.
Вывод в шаблоне
<?php
// single-book.php
$price = get_post_meta(get_the_ID(), 'book_price', true);
$pages = get_post_meta(get_the_ID(), 'book_pages', true);
if ($price !== '') {
echo '<p class="price">' . esc_html(number_format((float) $price, 0, ',', ' ')) . ' ₽</p>';
}
if ($pages !== '') {
echo '<p>Страниц: ' . (int) $pages . '</p>';
}Если поля нет, функция возвращает пустую строку.
Поле в редакторе
Чтобы редактор мог вводить цену, в форму записи добавляют блок:
// books.php
add_action('add_meta_boxes', function () {
add_meta_box('book_details', 'Сведения о книге', 'books_render_details', 'book', 'side');
});
function books_render_details(WP_Post $post): void
{
wp_nonce_field('book_details', 'book_details_nonce');
$price = get_post_meta($post->ID, 'book_price', true);
?>
<label for="book_price">Цена, ₽</label>
<input type="number" id="book_price" name="book_price" value="<?php echo esc_attr($price); ?>" min="0">
<?php
}wp_nonce_field() добавляет скрытое поле с токеном — он понадобится при сохранении.
Сохранение
// books.php
add_action('save_post_book', function (int $postId) {
if (!isset($_POST['book_details_nonce'])
|| !wp_verify_nonce($_POST['book_details_nonce'], 'book_details')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $postId)) {
return;
}
update_post_meta($postId, 'book_price', absint($_POST['book_price'] ?? 0));
});Три проверки: токен настоящий, это не автосохранение, у пользователя есть право менять запись. Значение приводится к целому числу функцией absint().
Поля в REST API
Чтобы поле было доступно блочному редактору и API, его регистрируют:
add_action('init', function () {
register_post_meta('book', 'book_price', [
'type' => 'integer',
'single' => true,
'show_in_rest' => true,
'sanitize_callback' => 'absint',
]);
});Готовый плагин
Для сайтов с десятками полей пользуются плагином Advanced Custom Fields: поля создают в панели, а в шаблоне читают функцией get_field('book_price'). Данные при этом лежат в тех же метаданных.
Поиск по полям
Записи можно отбирать и сортировать по значению поля — об этом в следующем уроке о WP_Query.
Частая ошибка
Обработчик save_post без проверок — уязвимость: любой запрос с нужным полем изменит данные чужой записи. Всегда проверяйте токен функцией wp_verify_nonce(), права функцией current_user_can() и очищайте значение перед записью — absint(), sanitize_text_field(), sanitize_email().