Метаданные в WordPress

Опубликовано: 01.09.2018

видео Метаданные в WordPress

Заголовки и метаданные Yoast Seo

Под словом «Метаданные» в WordPress скрывается огромная работа и потрясающая логика, которую можно ругать и хвалить одновременно, но одного у метаданных не отнять - они очень удобны. При этом разобраться в принципе их работы довольно просто.



Что такое метаданные?

Метаданные в WordPress — это данные, которые дополняют основные данные. Их еще называют метаполя, произвольные поля, кастомные поля. По-другому можно сказать, что метаданные - это дополнительная таблица в базе данных, которая расширяет основную таблицу.

Например:


Настройка Yoast WordPress SEO - Заголовки и мета-данные

у записи (поста) есть основные данные: контент, заголовок, ... и могут быть дополнительные данные (метаданные), например число просмотров записи, ID миниатюры, ID редактировавшего юзера, и т.д.

у пользователя есть основные данные - это данные из таблицы wp_users: логин, ссылка на сайт, email, ... и метаданные: биография, ссылка на соц. профиль, настройки админ панели.

Метаполя у записи

Метаданные пользователя

Метаданные могут быть у:

Записей - таблица wp_postmeta для wp_posts. Пользователей - таблица wp_usermeta для wp_users. Комментариев - таблица wp_commentmeta для wp_comments. Терминов (элементов таксономий) - таблица wp_termmeta для wp_terms. Сайтов (главный сайт сети в мультисайт) - таблица 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 ) .

Функции метаданных

Почти все функции метаданных работают на основе четырех базовых функций. На основе этих четырех функций по сути построено 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 ) 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).

Остальные функции связанные с регистрацией метаполей:

Метаполя для свой таблицы

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'; $res = $wpdb->get_results( "SELECT * 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 и более. С таким объемом данных в метаполях запросы уже будут заметно тормозить и без кэширования (которое не всегда подходит) или других надстроек уже будет не обойтись.

-

Читайте также:

IRC (Internet Relay Chat)
rss