Перейти до вмісту
🛠 Як додати одне довільне поле до багатьох постів у 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? Напишіть у коментарях, цікаво порівняти підходи.