Метадані у WordPress
Під словом “Метадані” в WordPress ховається величезна робота і приголомшлива логіка, яку можна лаяти і хвалити одночасно, але одного у метаданих не відібрати – вони дуже зручні. При цьому розібратися в принципі їхньої роботи дуже просто.
Читайте також, як влаштовані записи в WordPress
Що таке метадані?
Метадані в WordPress – це дані, які доповнюють основні дані. Їх ще називають метаполі, довільні поля, кастомні поля. Інакше можна сказати, що метадані – це додаткова таблиця у базі даних, яка розширює основну таблицю.
Наприклад:
у запису (посту) є основні дані: контент, заголовок, … і можуть бути додаткові дані (метадані), наприклад число переглядів запису, ID мініатюри, ID користувача, що редагував, і т.д.
- у користувача є основні дані – це дані з таблиці wp_users : логін, посилання на сайт, email, … та метадані: біографія, посилання на соц. профіль, налаштування адмін панелі.
Таблиці метаданих у базі даних WP
За промовчанням у WordPress існує 5 таблиць для різних об’єктів (записи, коментарі, користувачі, елементи таксономії, сайти):
- wp_postmeta
-
Записи – таблиця
wp_postmeta для
wp_posts . Сюди записуються звичні в WordPress “довільні поля посту” - wp_usermeta
-
Користувачі – таблиця
wp_usermeta для
wp_users . Додаткові дані про користувача. -
wp_termmeta
(З WP 4.4) -
Терміни (елементи таксономій) – таблиця
wp_termmeta для
wp_terms . Додаткові дані для елементів таксономії. - wp_commentmeta
-
Коментарі – таблиця
wp_commentmeta для
wp_comments . Метадані для кожного коментаря. -
wp_sitemeta
(для мультисайт збирання) -
Сайти (головний сайт мережі в мультисайті) – таблиця
wp_sitemeta для
wp_site . Опції основного веб-сайту мережі.
Усі метадані мають однакову логіку та таблиці однакової структури у базі даних.
Як бачимо, структура всіх таблиць однакова, різниця лише у назві основних полів. Зв’язуються вони з основною таблицею через друге поле (воно у всіх таблиць називається по-різному: post_id , user_id , comment_id , term_id ). Основний ключ (жовтий) зазвичай не використовується і потрібен лише для точної ідентифікації конкретного рядка в таблиці (це іноді потрібно, тому що зручно). Запити зазвичай працюють за трьома полями: 2, 3, 4.
За промовчанням WordPress активно використовує таблиці метаданих для записів та користувачів . Менш активно використовується таблиця метаданих для коментарів – туди при поміщенні коментаря до кошика записується час видалення (якщо кошик вимкнено, то таблиця не використовується). І зовсім не використовується таблиця метаданих для термінів .
Всі таблиці метаданих добре підходять для розширення можливостей двигуна. І їх активно використовують розробники плагінів та тем.
Таблиця метаданих для термінів була додана тільки у версії 4.4 спеціально для того, щоб розробникам було куди зберігати потрібні дані цих термінів.
Приховані (захищені) метаполі
У WordPress є таке поняття як приховані метаполі. Це поля, назва яких (значення meta_key ) починається з нижнього підкреслення _
. Так прийнято називати метаполя, які використовуються для потреб коду та не повинні змінюватися вручну.
В адмінці для постів у блоці «Довільні поля» приховані метаполя не відображаються, а значить їх не можна змінити. Так, наприклад, при редагуванні запису в метаполі _edit_lock записується мітка часу та ID користувача, який редагує запис. Завдяки цьому ми можемо бачити, що запис в даний момент редагується іншим користувачем. Або інший приклад, ID картинки-вкладення, яка встановлюється як мініатюра запису, зберігається в метаполі _thumbnail_id .
Для з’ясування, чи є метаполі прихованим, є спеціальна функція: is_protected_meta( $meta_key, $meta_type ) .
Щоб зробити будь-яке метаполі прихованим, є хук is_protected_meta :
// Прихуємо деякі метаполя add_filter( 'is_protected_meta', 'my_protected_custom_fields', 10, 2); function my_protected_custom_fields( $protected, $meta_key ){ if( in_array( $meta_key, [ 'luboe_pole' ] ) ) return true; return $protected; }
Функції метаданих
Майже всі функції метаданих працюють з урахуванням чотирьох базових функций. На основі цих чотирьох функцій по суті побудовано API роботи з усіма метаданими WordPress.
- get_metadata( $meta_type, $object_id, $key, $single )
- update_metadata( $meta_type, $object_id, $key, $value, $prev_value )
- delete_metadata( $meta_type, $object_id, $key, $value, $delete_all )
- add_metadata( $meta_type, $object_id, $key, $value, $unique )
Для записів:
- get_post_meta( $post_id, $key, $single )
- add_post_meta( $post_id, $key, $value, $unique )
- update_post_meta( $post_id, $key, $value, $prev_value )
- delete_post_meta( $post_id, $key, $value )
- delete_post_meta_by_key( $key ) – видаляє всі метаполя у всіх постів на ім’я поля.
- get_post_custom( $post_id ) – отримує масив всіх метаполів поточного посту (включаючи приховані).
- the_meta() – виводить значення метаполів запису у списку <li> (без прихованих).
Для користувачів:
- get_user_meta( $user_id, $key, $single )
- add_user_meta( $user_id, $key, $value, $unique )
- update_user_meta( $user_id, $key, $value, $prev_value )
- delete_user_meta( $user_id, $key, $value )
- get_the_author_meta( $field ) – отримує значення зазначеного метаполя поточного користувача.
Для коментарів:
- get_comment_meta( $comment_id, $key, $single )
- add_comment_meta( $comment_id, $key, $value, $unique )
- update_comment_meta( $comment_id, $key, $value, $prev_value )
- delete_comment_meta( $comment_id, $key, $value )
Для таксономій (термінів):
- get_term_meta( $term_id, $key, $single )
- add_term_meta( $term_id, $key, $value, $unique )
- update_term_meta( $term_id, $key, $value, $prev_value )
- delete_term_meta( $term_id, $key, $value )
- has_term_meta( $term_id ) – отримує масив всіх метаданих терміна (отримає всі поля таблиці).
Отримати значення всіх метаполів об’єкта можна за допомогою функції get_***_meta() . Для цього потрібно вказати лише перший параметр: id об’єкта:
$ Metas = get_post_meta (76); /* Array( [_edit_lock] => Array [0] => 1517175359:1 [_edit_last] => Array [0] => 1 9 views => Array [0] => 10164 [_thumbnail_id] => Array [0] => 9556 [photo] => Array [0] => https://wp-doc.com/wp-content/uploads/2010/03/Quicktags-API.png [1] => https://wp-doc.com/wp-content/uploads/2017/07/image.png ) */
Очищення значень метаполів при збереженні
Значення будь-якого метаполя можна очистити через фільтр: sanitize_(type)_meta_(meta_key) .
Цей фільтр завжди спрацьовує при додаванні або оновленні метаполя.
Усі варіанти фільтра, якщо вказати перший параметр, що змінюється:
- sanitize_post_meta_(meta_key)
- sanitize_user_meta_(meta_key)
- sanitize_comment_meta_(meta_key)
- sanitize_term_meta_(meta_key)
Приклад використання фільтра
Припустимо, ми маємо метаполі користувача my_history (моя історія). У цьому полі користувач може писати якийсь текст, але не можна використовувати HTML теги. Щоб бути впевненим, напевно, що туди не потраплять ці самі теги, найкраще очистити значення поля перед збереженням його до бази даних:
add_filter( 'sanitize_user_meta_'.'my_history', function( $meta_value ){ return wp_strip_all_tags( $meta_value ); });
Реєстрація метаполів
З версії 4.6 WordPress з’явилася можливість додатково описувати кожне метаполі. Це робиться через функцію register_meta() .
Реєстрація метаполя, потрібна для використання його в різних API, наприклад, для REST API (повна підтримка REST API очікується з версії WP 5.0).
Поведінка register_meta() чимось поході на register_post_type() – дані аналогічно зберігаються у глобальну змінну $wp_meta_keys . Це дозволяє отримувати дані метаполя у час під час написання коду.
Приклад реєстрація метаполя
Зареєструємо метаполі для посту з функціями доступу та очищення:
register_meta( 'post', 'bookname', [ 'type' => 'string', 'description' => 'Назва книги', 'single' => true, 'sanitize_callback' => function( $meta_value, $meta_key, $object_type ){ return wp_strip_all_tags( $meta_value ); // видалимо html теги }, 'auth_callback' => function( $false, $meta_key, $postID, $user_id, $cap, $caps ){ // Заборонимо створення та редагування цього метаполя для всіх, крім адміну return current_user_can('manage_options'); }, 'show_in_rest' => false, ]);
В результаті, якщо зайти на сторінку редагування запису і спробувати створити метаполі bookname :
- Якщо ви увійшли як адмін – метаполе створиться.
- Якщо редактор, автор тощо. – Ви не зможете створити метаполі.
За це відповідає параметрauth_callback
Далі, якщо в значенні вказати рядок з HTML-тегами, при оновленні вони будуть вирізані.
За це відповідає параметрsanitize_callback
Параметри: type
, description
, single
, show_in_rest
у версії WP 4.6, мають лише інформаційний характер. А з версії WP 5.0 використовуються REST API. Наприклад, якщо вказати show_in_rest=true
, метаполе можна буде редагувати через REST API.
Інші функції пов’язані з реєстрацією метаполів:
- registered_meta_key_exists()
- unregister_meta_key()
- get_registered_meta_keys()
- get_registered_metadata()
Метаполі для своєї таблиці
API метаполів дозволяє створити таблицю метаполів для будь-якої таблиці. Розглянемо з прикладу.
Допустимо у нас є таблиця my_books :
Створюємо для неї таблицю метаданих my_bookmeta :
Для створення таблиці метаданих потрібно один раз запустити написану нами функцію create_book_meta_table() . Код функції:
## register_activation_hook( __FILE__, 'create_book_meta_table'); ## Функція створення таблиці метаданих. Потрібно запустити один раз. Можна повісити register_activation_hook() function create_book_meta_table(){ Global $wpdb; $collate = ''; if ( ! empty($wpdb->charset) ) $collate = "DEFAULT CHARACTER SET $wpdb->charset"; if ( ! empty($wpdb->collate) ) $collate .= "COLLATE $wpdb->collate"; /* * Indexes має максимальний розмір з 767 bytes. Historically, we haven't необхідний be be concerned o that. * Як з 4.2, так, ми ведемо до utf8mb4, які використовують 4 bytes per character. This means that an index which * Використовується для того, щоб розташувати floor(767/3) = 255 characters, зараз тільки має room for floor(767/4) = 191 characters. */ $max_index_length = 191; $main_field = 'book_id'; // Назва головної колонки має виглядати як: $meta_type . '_id' $table_name = 'my_bookmeta'; $wpdb->query( "CREATE TABLE $table_name ( meta_id bigint(20) unsigned NOT NULL auto_increment, $main_field bigint(20) unsigned NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY $main_field ($main_field), KEY meta_key (meta_key($max_index_length)) ) $collate;" ); }
Назва головної колонки має виглядати як: $meta_type . '_id'
. Так воно збирається у функціях: *_metadata() .
Зареєструємо функції для роботи з метаданими book :
function add_book_meta( $id, $meta_key, $meta_value, $unique = false ) { return add_metadata( 'book', $id, $meta_key, $meta_value, $unique); } function delete_book_meta( $id, $meta_key, $meta_value = '' ) { return delete_metadata( 'book', $id, $meta_key, $meta_value); } function get_book_meta( $id, $meta_key = '', $single = false ) { return get_metadata( 'book', $id, $meta_key, $single); } function update_book_meta( $id, $meta_key, $meta_value, $prev_value = '' ){ return update_metadata( 'book', $id, $meta_key, $meta_value, $prev_value ); }
Всі!
Тепер ми можемо використовувати функції для керування метаданими, які будуть працювати в точності, як працюють метадані WordPress. Сюди входить і кешування метаданих, і всілякі хуки.
Наприклад:
// додамо дані до таблиці метаданих update_book_meta(12, 'author_name', 'Циркон'); // Отримаємо значення метаполя get_book_meta(12, 'author_name', 1); //> Циркон // Отримаємо значення всіх метполів get_book_meta(12); //> поверне масив
Побудова запиту з використанням метаданих
У WordPress дуже зручно можна вибирати або сортувати рядки основної таблиці на основі параметрів метаданих. Наприклад, WP_query() це робиться через параметр meta_query .
Прикрутити такий самий функціонал до нашої таблиці можна за допомогою класу WP_Meta_Query{} . Напишу, наприклад функцію отримання книг, з можливістю вибірки за метаданими:
// Встановимо таблиці в $wpdb Global $wpdb; $wpdb->books = "my_books"; $wpdb->bookmeta = "my_bookmeta"; ## Приклад функції для отримання книг, з можливістю вибірки метаданим function get_books( $args = array() ){ Global $wpdb; $default = [ 'book_id' => 0, 'name' => '', 'content_search' => '', // Мета-параметри, що розуміються. 'meta_key' => '', 'meta_value' => '', 'meta_value_num' => '', 'meta_compare' => '', 'meta_query' => array(), ]; $ args = array_merge ($ default, $ args); $WHERE = array(); $JOIN = $ORDER_BY = $LIMIT = ''; if( $args['book_id'] ){ // 'my_books.' потрібно тому що поле називається однаково у головної та у мета таблиці $WHERE[] = $wpdb->prepare('my_books.book_id = %d', $args['book_id'] ); } if( $args['name'] ){ $WHERE[] = $wpdb->prepare('name = %s', $args['name'] ); } if( $args['content_search'] ){ $WHERE[] = $wpdb->prepare('content LIKE %s', '%'. $wpdb->esc_like( $args['content_search'] ) .'%' ); } // мета запит if( $args['meta_query'] || $args['meta_key'] ){ $metaq = новий WP_Meta_Query(); $metaq->parse_query_vars( $args ); // парсим можливі мета-параметри з параметрів $args // перший параметр 'book' має бути початком властивості $wpdb->bookmeta без суффіка 'meta' // Тобто. ми вказуємо 'book' до нього додається 'meta' і властивість 'bookmeta' має існувати $wpdb // див. https://wp-doc.com/function/_get_meta_table $mq_sql = $metaq->get_sql( 'book', $wpdb->books, 'book_id'); $JOIN = $mq_sql['join']; // INNER JOIN my_bookmeta ON ( my_books.book_id = my_bookmeta.book_id ) $WHERE[] = $mq_sql['where']; // AND ( ( my_bookmeta.meta_key = 'author_name' AND my_bookmeta.meta_value = 'Циркон' ) ) } $WHERE = 'WHERE'. implode( 'AND', $WHERE ); /* для сортування за метаполями знадобиться $metaq->get_clauses() Array( [metasort] => Array( [key] => author_name [value] => Циркон [compare] => = [alias] => my_bookmeta [cast] => CHAR ) ) приклад дивіться у: https://wp-doc.com/function/WP_Query::parse_orderby */ $ORDER_BY = 'ORDER BY name ASC'; $fields = '*'; if( isset($metaq) && $metaq->has_or_relation() ) $fields = "DISTINCT $fields"; $res = $wpdb->get_results( "SELECT $fields FROM $wpdb->books $JOIN $WHERE $ORDER_BY $LIMIT" ); return $res; }
Перевіримо функцію, зробимо запит:
// Запит на отримання книг $books = get_books([ 'meta_key' => 'author_name', 'meta_value' => 'Циркон', ]); // або так $books = get_books([ 'meta_query' =>[ 'metasort' => [ 'key' => 'author_name', 'value' => 'Циркон', ] ] ]); print_r($books); /* Отримаємо: Array( [0] => stdClass Object( [book_id] => 12 [name] => Вишневий сад [content] => Зміст книги ... [meta_id] => 2 [meta_key] => author_name [meta_value] => Циркон ) ) */
Повторюся, тепер вибірку по метаполі можна робити будь-якої складності, все що дозволяє робити meta_query в WP_Query.
Приклад вибірки складніший:
$ args = array ( 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'author_name', 'value' => 'алекс', 'compare' => 'LIKE' ), array( 'key' => 'price', 'value' => array( 20, 100 ), 'type' => 'numeric', 'compare' => 'BETWEEN' ) ) ); $ books = get_books ($ args);
Продуктивність та метадані
Запити на основі meta_query відкривають величезні можливості, але у цього є зворотний бік – не найкраща продуктивність, особливо якщо складаються складні запити і обробляється велика кількість даних.
Слабкою ланкою метаданих є поле meta_value будь-якої таблиці метаданих (наприклад таблиці wp_postmeta ). meta_value немає і не може бути індексу, тому що поле має тип LONGTEXT , щоб можна було зберігати в ній будь-які дані: числа, тексти будь-якої довжини, серіалізовані масиви і т.д. Також індексування неможливе, тому що при побудові запиту, наприклад, сортування метаполем, де зберігаються числа, значення перетворюються з рядків у числа функцією CAST() і тільки потім сортуються. Такий підхід «убив» індекс, навіть якби він там був.
Через вищеописані особливості не завжди правильно використовувати метаполя для зберігання даних, за якими потім буде вибірка або сортування, хоча в більшості випадків вони підходять добре.
Завжди перевіряйте запити в реальних умовах, дивіться на час виконання і як змінюється цей час від кількості даних і параметрів запиту. Якщо запити стали повільними, настав час якось їх кешувати або підключати зовнішню систему індексування, наприклад Sphinx або Elasticsearch.
Якщо очікується зберігати великі обсяги даних, за якими потрібна буде вибірка та сортування, можливо варто створювати спеціальну таблицю під такі дані. Під великими обсягами даних я маю на увазі кількість значень одного мета-ключа від 20000 і більше. З таким обсягом даних у метаполях запити вже помітно гальмуватимуть і без кешування (яке не завжди підходить) або інших надбудов уже не обійдеться.
Як увімкнути довільні поля в Gutenberg?
–
Читайте також: