Перейти к содержимому
🛠 Как добавить одно произвольное поле ко многим постам в WordPress

🛠 Как добавить одно произвольное поле ко многим постам в WordPress

Сайт на WordPress разросся до сотен постов, и в каждом нужно прописать новое произвольное поле. Открывать каждый пост руками и жать «Обновить», занятие на полдня, не меньше.

Особенно если вы дорабатываете тему или подключаете SEO-плагин, которому для работы нужен одинаковый ключ meta_key у всей ленты постов. Админка WordPress не даёт кнопки «применить ко всем», но даёт два пути решить это кодом.

Ниже, оба способа с готовыми сниппетами: быстрый (один SQL-запрос на всю пачку) и простой (цикл по ID с нативной функцией add_post_meta). Выбирайте под задачу.

Два мони отображают различные блоки исходного кода и файлов разработки на рабочем месте разработчика

💡 Быстрый обзор:

  • Массово проставить одно и то же произвольное поле всем постам можно через add_post_meta в цикле, 4 строки кода.
  • Если постов сотни и важна скорость, один прямой INSERT в таблицу wp_postmeta через кастомную функцию.
  • Оба способа работают через functions.php темы или плагин Code Snippets. Сделайте бэкап базы перед запуском любого SQL.

Зачем добавлять одно поле ко многим постам сразу

Произвольные поля (custom fields, они же post meta) хранят дополнительную информацию о записи: количество просмотров, теги для внутренней фильтрации, флаг «в избранном», ID связанного продукта. Всё, что не влезает в стандартные поля post_title и post_content.

Обычно поле добавляют точечно: открыли пост, нашли блок «Произвольные поля» в редакторе, вписали ключ-значение. Но когда поле должно появиться у всех 200 постов разом, ручной метод отпадает.

Типичные сценарии, где нужна массовость:

  • Подключили дочернюю тему, а она использует get_post_meta($post_id, 'featured_post', true) для пометки популярных записей. Без массового заполнения старые посты не попадут в выдачу.
  • SEO-аудит потребовал проставить noindex определённому типу контента через метаполе, а не через robots.txt.
  • Миграция с другого движка: данные из старой CMS легли в CSV, и их нужно залить в wp_postmeta для тысяч записей.

В WordPress нет штатной функции add_multiple_posts_meta. Но есть два проверенных пути, выбирайте по ситуации.

Способ 1. Один SQL-запрос для всех постов

Самый быстрый способ, прямой INSERT в таблицу wp_postmeta одной транзакцией. Вместо 200 отдельных запросов к базе вы делаете один, и он отрабатывает за миллисекунды.

Ниже, функция add_multiple_posts_meta, которая принимает массив ID постов и повторяет сигнатуру нативной add_post_meta: те же параметры, тот же порядок.

Добавьте код в functions.php дочерней темы или через плагин Code Snippets. Функция сама проверяет, существует ли уже такое поле у поста (если передан $unique = true), и отфильтровывает невалидные ID.

1if( !function_exists( 'add_multiple_posts_meta' ) ){
2 function add_multiple_posts_meta( $post_ids, $meta_key, $meta_value, $unique = false ){
3
4 // Если ключ или ID не заданы — выходим
5 if( !$meta_key || empty( $post_ids ) ){
6 return false;
7 }
8
9 // Один пост — отдаём нативной add_post_meta
10 if( is_numeric( $post_ids ) || ( is_array( $post_ids ) && count( $post_ids ) === 1 ) ){
11 return add_post_meta(
12 is_array( $post_ids ) ? $post_ids[0] : $post_ids,
13 $meta_key,
14 $meta_value,
15 $unique
16 );
17 }
18
19 global $wpdb;
20
21 // Отсеиваем невалидные ID и дубликаты
22 $valid_post_ids = array_unique(
23 array_filter( $post_ids, function( $post_id ) use ( $wpdb, $meta_key, $unique ){
24 return $post_id
25 && is_numeric( $post_id )
26 && get_post_status( $post_id )
27 && (
28 ! $unique
29 || ! $wpdb->get_var( $wpdb->prepare(
30 "SELECT COUNT(*) FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s",
31 $post_id,
32 $meta_key
33 ) )
34 );
35 } )
36 );
37
38 if( ! count( $valid_post_ids ) ){
39 return false;
40 }
41
42 // Санитизация
43 $meta_key = wp_unslash( $meta_key );
44 $meta_value = sanitize_meta( $meta_key, wp_unslash( $meta_value ), 'post' );
45
46 // Собираем данные и форматные строки для одного INSERT
47 $insert_data = [];
48 $insert_formats = [];
49
50 foreach( $valid_post_ids as $post_id ){
51 array_push( $insert_data, $post_id, $meta_key, $meta_value );
52 $insert_formats[] = '(%d, %s, %s)';
53 }
54
55 $sql_string = "INSERT INTO $wpdb->postmeta (`post_id`, `meta_key`, `meta_value`) VALUES "
56 . implode( ', ', $insert_formats );
57
58 // ... распаковывает массив в отдельные аргументы для prepare
59 $sql_query = $wpdb->prepare( $sql_string, ...$insert_data );
60
61 return $wpdb->query( $sql_query );
62 }
63}

Что здесь происходит построчно. Проверка !function_exists, страховка от конфликта: если другой плагин уже объявил функцию с таким именем, фатальной ошибки не будет. Блок is_numeric($post_ids) отдаёт одиночный ID нативной add_post_meta, так одна и та же функция работает и с одним постом, и с массивом. Фильтр array_filter отбрасывает ID черновиков, удалённых записей и те, для которых уникальное поле уже существует. Санитизация через wp_unslash и sanitize_meta обязательна: без неё сериализованные данные и спецсимволы поломают запрос.

*Важный момент с ...$insert_data.* Оператор ... (splat) разворачивает массив в плоский список аргументов. Без него $wpdb->prepare получит один аргумент-массив вместо набора (%d, %s, %s), и запрос упадёт с ошибкой количества плейсхолдеров. PHP 5.6+ (WordPress требует минимум 7.0, так что смело).

Как использовать

Передайте массив ID постов, ключ и значение обычным вызовом. Четвёртый параметр, флаг уникальности: true не добавит поле, если оно уже есть у поста.

1$post_ids = [ 1, 2, 3, 42, 108 ];
2$meta_key = 'featured_priority';
3$meta_value = 'high';
4$unique = false; // проставит всем, даже если поле уже было
5
6add_multiple_posts_meta( $post_ids, $meta_key, $meta_value, $unique );

Запускать такой вызов удобно через хук init (один раз, потом удалить) или через WP-CLI командой wp eval-file. После выполнения обязательно уберите код из functions.php, иначе функция будет срабатывать при каждой загрузке страницы.

Способ 2. Цикл по ID с add_post_meta

Если не хотите трогать SQL напрямую, используйте нативный add_post_meta в цикле. Код вчетверо короче и полностью опирается на ядро WordPress.

1$post_ids = [ 1, 2, 3, 42, 108 ];
2$meta_key = 'featured_priority';
3$meta_value = 'high';
4$unique = false;
5
6foreach( $post_ids as $post_id ){
7 add_post_meta( $post_id, $meta_key, $meta_value, $unique );
8}

Цикл проходит по массиву и для каждого ID дёргает add_post_meta. Функция сама проверяет существование записи, уникальность ключа (если задан $unique = true) и корректность данных, никакой дополнительной валидации не нужно.

Производительность. На сотне постов разница с SQL-запросом незаметна. Но если у вас 5 000 записей, каждый вызов add_post_meta порождает отдельный INSERT, плюс внутренние хуки (added_post_meta, update_post_meta_cache). На таких объёмах первый способ с одним запросом отработает в 10-20 раз быстрее.

Для разовых операций с парой десятков постов, берите цикл, не усложняйте.

Сравнение подходов

Критерий

Способ 1 (один SQL)

Способ 2 (цикл add_post_meta)

Скорость на 5 000 постов

~0,2 сек (1 запрос)

~3-6 сек (5 000 запросов)

Сложность кода

~50 строк с валидацией

4 строки

Безопасность

Нужна ручная санитизация

add_post_meta делает всё сам

Хуки WordPress

Не вызываются

added_post_meta, update_post_meta_cache

Когда выбирать

Массовая миграция, импорт из CSV

Разовые правки, до сотни постов

Универсальное правило: если постов меньше ста и операция разовая, цикл. Если речь о регулярной синхронизации или миграции тысяч записей, только прямой запрос с подготовленной функцией.

⁉️🤔 Частые вопросы

Можно ли проставить поле выборочно, только постам из определённой рубрики?

Да. Получите ID постов через get_posts с параметром 'category' => ID_рубрики, а затем передайте массив в любой из двух способов выше. Для тысяч постов добавьте 'numberposts' => -1 и 'fields' => 'ids', запрос вернёт только массив ID, без загрузки объектов.

Вот готовый пример: $post_ids = get_posts( [ 'category' => 5, 'numberposts' => -1, 'fields' => 'ids', 'post_type' => 'post' ] );. Дальше, вызов add_multiple_posts_meta($post_ids, ...) или цикл с add_post_meta.

Что делать, если нужно разное значение поля для разных постов?

Массовая вставка здесь не подойдёт. Формируйте ассоциативный массив [ ID_поста => значение ] и проходите по нему в цикле с add_post_meta или update_post_meta. Одно значение для всех, SQL, разные для разных, только цикл.

Для 50+ постов с уникальными значениями используйте update_post_meta вместо add_post_meta: он не плодит дубликаты, если поле уже существует.

Безопасно ли писать SQL-запросы напрямую в wp_postmeta?

Безопасно при двух условиях. Первое: всегда прогоняйте данные через $wpdb->prepare, он экранирует спецсимволы и блокирует SQL-инъекции. Второе: сделайте полный бэкап базы перед запуском. Один неверный UPDATE без WHERE, и wp_postmeta очищена для всех записей.

Команда для бэкапа через WP-CLI: wp db export backup-before-meta.sql. Восстановить: wp db import backup-before-meta.sql.

Чем add_post_meta отличается от update_post_meta?

add_post_meta добавляет новую строку в wp_postmeta, даже если ключ уже есть у поста, получится дубликат. update_post_meta проверяет существование ключа: если есть, обновляет значение, если нет, создаёт. Для массового заполнения, где ключ один на пост, add_post_meta с $unique = true даёт тот же эффект.

На практике: add_post_meta($id, 'views', 0, true) один раз создаст поле и больше не тронет. update_post_meta($id, 'views', $count) перезапишет значение при каждом вызове.

Где размещать код, в теме или плагине?

Для разовых операций, в functions.php дочерней темы, с немедленным удалением после выполнения. Для повторяющихся задач, оформите как Must-Use плагин (папка wp-content/mu-plugins/) или добавьте в Code Snippets с пометкой «Run once». Код в активной теме без удаления будет дёргать базу при каждой загрузке страницы — это лишняя нагрузка.

Что взять под вашу задачу

Практика показывает: 9 из 10 случаев закрываются циклом с add_post_meta. Это четыре строки кода, нативная функция и никаких сюрпризов.

Прямой SQL-запрос оправдан в двух ситуациях: массовая миграция (тысячи постов, время дорого) и периодическая синхронизация, где вы контролируете санитизацию на входе. В остальном, не усложняйте.

Что делать прямо сейчас:

  • Если нужно проставить поле ≤100 постам, скопируйте цикл из второго способа, подставьте свои ID и запустите.
  • Если постов несколько тысяч, возьмите функцию из первого способа, но сначала оттестируйте на копии сайта.
  • В любом варианте, бэкап базы перед запуском. Это не предосторожность, а правило.

А каким способом вы обычно добавляете произвольные поля: через админку, код или плагин вроде ACF? Напишите в комментариях, интересно сравнить подходы.