Получить meta_id вместе с meta_key и meta_value
Возможно ли как-то получить с помощью get_post_meta
или get_post_custom
значение meta_id вместе с meta_value
? Например:
$data = get_post_meta( $post_id, 'my_key' );
// возвращает следующее:
array( 0 => array('myvalue1', 1002 ), 1 => array( 'myvalue2', 1003 ));
Основная идея в том, что поскольку для одного meta_key
может быть несколько meta_values, мне нужно знать meta_id
, чтобы надежно обновлять/удалять мета-данные записи.

Эта функция сработала для меня:
function get_complete_meta( $post_id, $meta_key ) {
global $wpdb;
$mid = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key) );
if( $mid != '' )
return $mid;
return false;
}
Она вернет массив объектов вида:
Array
(
[0] => stdClass Object
(
[meta_id] => 1002
[post_id] => 1
[meta_key] => my_key
[meta_value] => my_value
)
[1] => stdClass Object
(
[meta_id] => 1003
[post_id] => 668
[meta_key] => my_key
[meta_value] => another value
)
)

Я не знаю о существовании в ядре WordPress функции/метода для получения метаданных записи по ключу. Это не значит, что такой функции точно нет. Возможно, она существует. Я не знаю всего о WordPress, я просто делаю вид :) Или, может быть, я просто не могу вспомнить её сейчас.
Однако, четвертый параметр функции update_post_meta
предназначен для того, чтобы гарантировать обновление только нужного значения в случаях, когда существует несколько записей с одинаковым ключом.
$prev_value
(mixed) (необязательный) Старое значение произвольного поля, которое вы хотите изменить. Это позволяет различить несколько полей с одинаковым ключом. Если параметр опущен, и существует несколько строк для этой записи и мета-ключа, все мета-значения будут обновлены.
Вы передаете ранее сохраненное значение в этом четвертом параметре, и тогда обновится только соответствующая запись.
Функция delete_post_meta
работает аналогично, но с третьим параметром вместо четвертого.

@s_ha_dum как вы считаете, какое отношение этот ответ имеет к вопросу? Автору нужно получить meta_id по meta_key и post_id, что и будет возвращать моя функция.

Автор вопроса пишет: "Основная идея в том, что поскольку для одного meta_key может быть несколько meta_values, мне нужно знать meta_id, чтобы надежно обновлять/удалять метаданные записи." То есть проблема/озабоченность связана с надежным обновлением метаданных при наличии нескольких ключей. Часть, где говорится "мне нужно знать meta_id, чтобы надежно обновлять/удалять метаданные записи" - неверна. Это не обязательно, и WordPress Core использует другой подход к решению этой проблемы.

Я считаю, что у автора вопроса проблема XY, другими словами.

Я прихожу к аналогичному выводу в своем ответе: http://wordpress.stackexchange.com/a/110780/178

Я действительно пробовал это решение, но кажется несколько неудобным полагаться на значение meta при работе с сериализованными meta значениями. Я надеялся, что есть способ получить meta_id вместе с meta_values, чтобы можно было использовать update_metadata_by_mid
и delete_metadata_by_mid

Добавьте этот код в ваш functions.php
function get_mid_by_key( $post_id, $meta_key ) {
global $wpdb;
$mid = $wpdb->get_var( $wpdb->prepare("SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key) );
if( $mid != '' )
return (int) $mid;
return false;
}
и затем вызывайте где угодно, например
$meta_id = get_mid_by_key( your_post_id, 'your_meta_key' );

Странно, что WordPress предоставляет функции для получения, обновления и удаления метаданных по meta_id начиная с версии 3.3, но по состоянию на версию 3.7 не имеет функций, возвращающих meta id. Поэтому, используя основные функции из meta.php в качестве примера, я реализовал приведенные ниже функции для возможности получения meta id вместе со значениями.
Решение
Используйте пользовательскую функцию get_post_meta_db()
для получения идентификаторов и значений мета-ключей, а затем функции WordPress update_meta()
и delete_meta()
для управления метаданными записи.
Например:
$meta = get_post_meta_db( $post_id, 'my_key', true ); // Возвращает массив(
// 'post_id' => 5,
// 'meta_id' = 33,
// 'meta_key' => 'my_key',
// 'meta_value' => 'the_value_for_my_key' );
update_meta( $meta['meta_id'], $meta['meta_key'], 'new_value' );
Вот определения пользовательских функций:
/**
* Альтернатива get_post_meta(), для получения meta_ids. @see get_meta_db()
*/
function get_post_meta_db( $post_id, $meta_key = null, $single = false, $meta_val = null, $output = OBJECT ){
return get_meta_db( 'post', $post_id, $meta_key, $meta_val, $single, $output );
}
/**
* Альтернатива get_metadata(). Отличия:
* - возвращает все поля метаданных (вместо только meta_values)
* - обходит фильтры/действия метаданных
* - запрашивает базу данных, минуя кеш
* - возвращает сырые meta_values (без преобразования сериализованных массивов)
*
* @param string $meta_type Тип объекта метаданных (например, comment, post или user)
* @param int $object_id ID объекта метаданных
* @param string $meta_key Опционально. Ключ метаданных для получения. По умолчанию возвращает все метаданные указанного объекта.
* @param mixed $meta_val Опционально. Если указано, возвращает только строки с этим значением meta_value.
* @param bool $single Опционально. Если true, возвращает одну строку, иначе массив строк.
* @param string $output Опционально. Любая из констант ARRAY_A | ARRAY_N | OBJECT | OBJECT_K. @see wpdb::get_results()
*
* @return array Одна строка метаданных, массив строк, пустой массив при отсутствии совпадений или false при ошибке.
*/
function get_meta_db( $meta_type, $object_id = null, $meta_key = null, $meta_val = null, $single = false, $output = OBJECT ){
if( !$meta_type || !$table = _get_meta_table( $meta_type ) )
return false;
// Создание запроса
global $wpdb;
$query = $wpdb->prepare( "SELECT * FROM $table", $object_id );
// Добавление условий в запрос
$where = array();
if( $object_id = absint( $object_id ) )
$where[] = $wpdb->prepare( sanitize_key( $meta_type.'_id' ).' = %d', $object_id );
if( !empty($meta_key) )
$where[] = $wpdb->prepare( 'meta_key = %s', wp_unslash( $meta_key ) );
if( null !== $meta_val )
$where[] = $wpdb->prepare( 'meta_value = %s', maybe_serialize(wp_unslash($meta_val)));
if( !empty($where) )
$query .= ' WHERE '.implode(' AND ', $where );
if( $single )
$query .= ' LIMIT 1';
$rows = $wpdb->get_results( $query, $output );
if( empty( $rows ) )
return ( $single ? null : array() );
/*
Раскомментируйте этот раздел для автоматического преобразования сериализованных значений (как в get_metadata())
ПРИМЕЧАНИЕ: Это можно реализовать более эффективно с помощью array_map
// Преобразование сериализованных meta_values
foreach( $rows as &$r ){
$v =& ($output==ARRAY_A ? $r['meta_value'] : $output==ARRAY_N ? $r[3] : $r->meta_value );
$v = maybe_unserialize( $v );
} */
return ( $single ? reset( $rows ) : $rows );
}
Примечания:
- Эти функции выполняют запросы к базе данных, поэтому используйте их умеренно!
- Эта реализация следует соглашению, используемому в
get_metadata_by_mid()
, не применяя предварительные фильтры"get_post_metadata"
/"get_user_metadata"
и не обновляя кеш. - В моем случае требовались только сырые данные, поэтому, в отличие от
get_metadata_by_mid()
, эта реализация не преобразует сериализованные значения meta. - Эти функции позволяют выбирать метаданные по post_id/user_id, meta key, meta value или любой их комбинации.

Основная идея в том, что поскольку может быть несколько значений
meta_value
для одного и того жеmeta_key
, мне нужно знатьmeta_id
, чтобы надежно обновлять/удалять метаданные записи.
То, что вы называете meta_id
, технически не существует в WordPress. Есть post_id
, и это же идентификатор для всех метаданных (в терминах мета-системы это object_id
).
Помимо этого есть meta_key
, и, как вы правильно отметили, поскольку для одного object_id
может быть несколько значений для каждого meta_key
, возникает неоднозначность, потому что все зависит от порядка, в котором база данных представляет эти значения — и то лишь косвенно, так как метаданные также имеют свой кеш — object cache.
Даже если вы видите meta_id
в базе данных, он дальше не используется, только как первичный ключ в БД.
Он не является частью результата запроса, который используется в PHP-коде WordPress на уровне пользователя, поэтому не входит в мета-кеш и никогда не попадает в API функций метаданных (потому что они обращаются к базе данных через API мета-кеша).
Что приводит нас к окончательному выводу:
Поскольку вы говорите о meta_id
, который существует только в базе данных (таблица dbprefix_postmeta
), ваш вопрос может означать только удаление/обновление записи в этой таблице. Для этого достаточно обычных SQL-операций.

Основная функция WordPress has_meta
возвращает ассоциативные массивы метаданных. Каждый массив метаданных содержит meta_id
.
Возвращаемое значение
(array[]) Массив массивов метаданных для указанного ID записи.
- '...$0' (array) Ассоциативный массив метаданных.
- 'meta_key' (string) Мета-ключ.
- 'meta_value' (mixed) Мета-значение.
- 'meta_id' (string) ID метаданных в виде строки с числом.
- 'post_id' (string) ID записи в виде строки с числом.
Вы можете надежно обновлять и удалять мета-значения с одинаковыми мета-ключами, используя has_meta
вместе с update_metadata_by_mid
и delete_metadata_by_mid
.
В примере автора вопроса, чтобы обновить array('myvalue1', 1002)
и удалить array('myvalue2', 1003)
:
$metadata = has_meta( $post_id );
foreach ( $metadata as $item ) {
// Конвертируем строку meta_id в целое число
$meta_id = intval( $item[ 'meta_id' ] );
$meta_key = $item[ 'meta_key' ];
$meta_value = $item[ 'meta_value' ];
if ( $meta_key === 'my_key' && $meta_value === 'myvalue1' ) {
update_metadata_by_mid( 'post', $meta_id, 'new value' );
} else if ( $meta_key === 'my_key' && $meta_value === 'myvalue2' ) {
delete_metadata_by_mid( 'post', $meta_id );
}
}
Справка: has_meta
используется в ядре WordPress для заполнения полей формы в панели "Произвольные поля".
