получение всех значений для ключа произвольного поля (кросс-пост)

15 февр. 2011 г., 15:01:03
Просмотры: 66.9K
Голосов: 54

Я знаю, как получить значение произвольного поля для конкретной записи.

get_post_meta($post_id, $key, $single);

Мне нужно получить все значения, связанные с определенным ключом произвольного поля, во всех записях.

Кто-нибудь знает эффективный способ сделать это? Я бы не хотел перебирать все ID записей в базе данных.

Пример:

4 записи, все с разными значениями для произвольного поля 'Mood'. 2 записи имеют значение 'happy', 1 запись имеет 'angry' и 1 запись имеет 'sad'

Я хочу вывести: во всех записях у нас: два счастливых, один сердитый и один грустный автор(ов).

Но для МНОЖЕСТВА записей.

Я ищу либо:

  • функцию WP для получения этого. или
  • пользовательский запрос для максимально эффективного получения этого.
2
Комментарии

Похоже, вы используете это как таксономию. Почему бы просто (автоматически) не добавлять термин к этим записям при сохранении? Это значительно упростит запросы.

kaiser kaiser
15 февр. 2011 г. 17:11:16

@kaiser Не могу выразить, насколько я благодарен за то, что ты гений!

user2128576 user2128576
27 окт. 2016 г. 00:24:02
Все ответы на вопрос 7
3
65

Один из возможных подходов — использовать один из вспомогательных методов класса WPDB для выполнения более точного запроса на основе метаданных.

Используя функцию $wpdb get_col, можно получить простой плоский массив данных.

Вот пример функции, которая запрашивает из базы данных все записи указанного типа, статуса и мета-ключа (или пользовательского поля, для менее технически подкованных).

function get_meta_values( $meta_key = '', $post_type = 'post', $post_status = 'publish' ) {
    
    global $wpdb;
    
    if( empty( $meta_key ) )
        return;
    
    $meta_values = $wpdb->get_col( $wpdb->prepare( "
        SELECT pm.meta_value FROM {$wpdb->postmeta} pm
        LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
        WHERE pm.meta_key = %s 
        AND p.post_type = %s 
        AND p.post_status = %s 
    ", $meta_key, $post_type, $post_status ) );
    
    return $meta_values;
}

Например, если вы хотите узнать, какие записи имеют мета-ключ rating для типа записи movies, и сохранить эту информацию в переменной, вызов функции будет выглядеть так:

$movie_ratings = get_meta_values( 'rating', 'movies' );

Если вам нужно просто вывести эти данные на экран, функция PHP implode может быстро преобразовать массив в строку.

// Выводим мета-значения, разделенные переносом строки
echo implode( '<br />', get_meta_values( 'YOURKEY' ));

Вы также можете использовать возвращенные данные, чтобы подсчитать, сколько записей имеют определенные мета-значения, выполнив простую итерацию (цикл) по данным и создав массив с количеством, например:

$movie_ratings = get_meta_values( 'rating', 'movies' );
if( !empty( $movie_ratings ) ) {
    $num_of_ratings = [];
    foreach( $movie_ratings as $meta_value ) {
        $num_of_ratings[$meta_value] = isset( $num_of_ratings[$meta_value] ) ? $num_of_ratings[$meta_value] + 1 : 1;
    }
}

// Выводим количество оценок
printf( '<pre>%s</pre>', print_r( $num_of_ratings ) );  

/*
Вывод:
Array(
    [5] => 10
    [9] => 2
)
т.е. 10 записей фильмов имеют оценку 5 и 2 записи — оценку 9.
*/

Эту логику можно применить к различным типам данных и расширить для работы разными способами. Надеюсь, мои примеры были полезны и достаточно просты для понимания.


Использование транзитов для кэширования результатов

А вот обновленная версия, которая использует WordPress-транзиты для кэширования запроса, так как это основная критика использования $wpdb в других предложенных ответах.

function get_meta_values( string $meta_key, string $post_type = 'post', bool $distinct = false, string $post_status = 'publish' ) {
    
    global $wpdb, $wp_post_types;
    
    if( !isset( $wp_post_types[$post_type] ) )
        // Существующая строка WordPress, она должна переводиться как есть
        return __( 'Invalid post type.' ); 
    
    $transient_key = 'get_' . $wp_post_types[$post_type]->name . '_type_meta_values';
   
    $get_meta_values = get_transient( $transient_key );

    if( true === (bool)$get_meta_values )
        return $get_meta_values;
    
    $distinct = $distinct ? ' DISTINCT' : '';
    
    $get_meta_values = $wpdb->get_col( $wpdb->prepare( "
        SELECT{$distinct} pm.meta_value FROM {$wpdb->postmeta} pm 
        LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id 
        WHERE pm.meta_key = %s 
        AND p.post_type = %s 
        AND p.post_status = %s 
    ", $meta_key, $post_type, $post_status ) );
    
    set_transient( $transient_key, $get_meta_values, DAY_IN_SECONDS );

    return $get_meta_values;
}

Константа DAY_IN_SECONDS — одна из нескольких констант времени в секундах, установленных WordPress.

Обновлены имена аргументов и переменных, чтобы сделать их более согласованными с именованием WordPress, а также добавлен необязательный параметр DISTINCT по совету Howdy_McGee в комментариях.

16 февр. 2011 г. 00:48:36
Комментарии

Также забавный факт для будущих читателей: если вы хотите получить только уникальные значения мета-полей - вы пишете DISTINCT сразу после SELECT в функции выше. Может быть полезно.

Howdy_McGee Howdy_McGee
7 февр. 2014 г. 20:24:07

Я думаю, это чрезвычайно полезно

Pablo S G Pacheco Pablo S G Pacheco
1 мая 2017 г. 17:54:08

Как это сделать и вернуть значения отсортированными? Думаю, нужно использовать ORDER BY, но не могу понять, как именно

efirvida efirvida
17 апр. 2018 г. 06:43:28
4
20

Не рекомендуется и не нужно использовать глобальную переменную $wpdb:

// функция для получения всех возможных мета-значений выбранного мета-ключа.
function get_meta_values( $meta_key,  $post_type = 'post' ) {

    $posts = get_posts(
        array(
            'post_type' => $post_type,
            'meta_key' => $meta_key,
            'posts_per_page' => -1,
        )
    );

    $meta_values = array();
    foreach( $posts as $post ) {
        $meta_values[] = get_post_meta( $post->ID, $meta_key, true );
    }

    return $meta_values;

}

$meta_values = get_meta_values( $meta_key, $post_type );
4 окт. 2015 г. 00:32:20
Комментарии

Это мой предпочтительный метод в большинстве случаев. Он выполняет пять запросов вместо одного, но, поскольку используются стандартные процедуры WordPress для их генерации и отправки, будет задействовано любое кеширование, специфичное для платформы (например, Object Caching от WP Engine или какой-нибудь плагин). Данные также будут храниться во внутреннем кеше WordPress на время запроса, поэтому не потребуется извлекать их из базы данных повторно, если они понадобятся снова.

Andrew Dinmore Andrew Dinmore
2 июн. 2017 г. 18:33:49

К данным также будут применены все фильтры, что может быть крайне важно, например, на многоязычном сайте. Наконец, поскольку используются только стандартные функции ядра WordPress, вероятность того, что это сломается в будущем обновлении, гораздо ниже.

Andrew Dinmore Andrew Dinmore
2 июн. 2017 г. 18:34:59

Производительность можно улучшить, ограничив запрос только ID записей?

Добавить: 'fields' => 'ids'

Таким образом, массив запроса будет выглядеть так:

array( 'post_type' => $post_type, 'meta_key' => $meta_key, 'posts_per_page' => -1, 'fields' => 'ids' )

Pea Pea
13 апр. 2020 г. 21:31:16

Внимание: это также отфильтровывает мета-значения, которые существуют только в неопубликованных записях, поэтому убедитесь, что используете аргумент 'post_status', чтобы это было функцией, а не ошибкой

jnhghy - Alexandru Jantea jnhghy - Alexandru Jantea
24 июл. 2020 г. 01:25:19
1
14

Я хотел бы добавить одну небольшую деталь к коду t31os, приведённому выше. Я заменил "SELECT" на "SELECT DISTINCT", чтобы исключить дублирующиеся записи, когда использовал этот код.

5 июн. 2011 г. 21:52:28
Комментарии

Я могу представить случаи, когда вполне допустимо иметь несколько метазначений с одинаковым значением, поэтому я не стал вносить это изменение в свой код. Если вам нужны уникальные значения, то это был бы правильный подход. Кроме того, вы также можете добавить это в качестве аргумента функции (чтобы использовать или не использовать эту возможность по мере необходимости).

t31os t31os
24 янв. 2014 г. 20:19:02
2

Для получения всех мета-значений по ключу

Проверьте wp->db в WordPress Codex

$values = $wpdb->get_col("SELECT meta_value
    FROM $wpdb->postmeta WHERE meta_key = 'yourmetakey'" );
7 сент. 2013 г. 14:07:25
Комментарии

Проблема этого подхода в отсутствии специфичности — такой запрос вернёт множество результатов, которые могут включать черновики, удалённые записи, посты, страницы и любые другие типы записей. Вы никогда не должны запрашивать то, что вам не нужно, здесь определённо требуется конкретность.

t31os t31os
24 янв. 2014 г. 20:23:09

Хотя верно, что вы можете получить значения из других типов записей и их статусов, бывают случаи, когда вам нужны только значения и вы не использовали этот meta_key нигде, кроме нужного места. Если все или большинство значений уникальны, это может быть лучшим решением.

Luke Gedeon Luke Gedeon
23 июн. 2018 г. 21:36:44
3

самым быстрым способом будет пользовательский SQL-запрос, и я не уверен, но вы можете попробовать

$wpdb->get_results("
  SELECT posts.* , COUNT(*) 'moodcount'
  FROM $wpdb->posts as posts
  JOIN $wpdb->postmeta as postmeta
  ON postmeta.post_id = posts.ID
  AND postmeta.meta_key = 'Mood'
  GROUP BY postmeta.meta_key
");

Если что, то это начало.

15 февр. 2011 г. 17:01:49
Комментарии

спасибо, но разве не следует избегать пользовательских запросов 'любой ценой'? Я бы предпочел использовать уровень абстракции WP (так это называется?)... но конечно, если это невозможно..

mikkelbreum mikkelbreum
8 июн. 2011 г. 13:53:26

Пользовательские запросы, если они написаны правильно, могут быть лучше, и вам следует избегать их, только если вы не знаете, что делаете.

Bainternet Bainternet
8 июн. 2011 г. 16:07:58

Я согласен с mwb. Пользовательские запросы очень полезны и практичны, но я думаю, что они также значительно нагружают базу данных.. особенно при использовании SRT функций..

krembo99 krembo99
10 дек. 2011 г. 05:20:54
0

Нет никаких причин, почему нельзя объединить код t31os и Bainternet, чтобы получить переиспользуемое подготовленное выражение (в стиле WordPress), которое возвращает и количество, и значения в одной эффективной операции.

Это пользовательский запрос, но он всё ещё использует уровень абстракции базы данных WordPress — так что, например, неважно, как на самом деле называются таблицы или если они изменятся, и это подготовленное выражение, что делает его значительно безопаснее против SQL-инъекций и т.д.

В данном случае я больше не проверяю тип записи и исключаю пустые строки:

    $r = $wpdb->get_results(  $wpdb->prepare( "
        SELECT pm.meta_value AS name, count(*) AS count  FROM {$wpdb->postmeta} pm
        LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
        WHERE pm.meta_key = '%s'
        AND pm.meta_value != '' 
        AND p.post_type = '%s'
        GROUP BY pm.meta_value
        ORDER BY pm.meta_value          
        ", $key, $type) 
        );
    return $r;

В этом конкретном случае

Это вернёт массив объектов вида:

array  
 0 => 
 object(stdClass)[359]
  public 'name' => string 'Hamish' (length=6)
  public 'count' => string '3' (length=1)
 1 => 
 object(stdClass)[360]
  public 'name' => string 'Ida' (length=11)
  public 'count' => string '1' (length=1)
 2 => 
 object(stdClass)[361]
  public 'name' => string 'John' (length=12)
  public 'count' => string '1' (length=1)
31 янв. 2012 г. 15:23:34
2

Используйте следующий код с foreach

 $key = get_post_custom_values( 'key' );

Предполагается, что имя вашего пользовательского поля key

29 июл. 2017 г. 14:06:22
Комментарии

Обратите внимание, что по умолчанию используется текущая запись, если не указан post_id.

birgire birgire
29 июл. 2017 г. 14:23:46

Это просто возвращает все пользовательские поля для одной записи, которая по умолчанию имеет ID "0". Обратите внимание, что документация явно указывает, что "параметры нельзя считать необязательными".

Husky Husky
24 февр. 2022 г. 15:24:00