Мета-данные в WordPress
Под словом «Метаданные» в 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 [views] => Array [0] => 10164 [_thumbnail_id] => Array [0] => 9556 [photo] => Array [0] => https://wp-kama.ru/wp-content/uploads/2010/03/Quicktags-API.png [1] => https://wp-kama.ru/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 have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
* As of 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
* used to have room for floor(767/3) = 255 characters, now only has 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 = new WP_Meta_Query();
$metaq->parse_query_vars( $args ); // парсим возможные мета-параметры из параметров $args
// первый параметр 'book' должен быть началом свойства $wpdb->bookmeta без суффика 'meta'
// Т.е. мы указываем 'book' к нему добавляется 'meta' и свойство 'bookmeta' должно существовать в $wpdb
// см. https://wp-kama.ru/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-kama.ru/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] => Циркон ) ) */
Повторюсь, теперь выборку по метаполям можно делать любой сложности, все что позволяет делать WP_Meta_Query{}, оно же 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?
-
Читайте также:






