Может ли wp_query вернуть мета-данные записей в одном запросе?

12 дек. 2014 г., 05:09:07
Просмотры: 44.3K
Голосов: 27

Я хотел бы создать wp_query, который возвращал бы мета-данные записей внутри массива posts.

$args = array (
    'post_type' => 'page',
    'meta_key' => 'someMetaKeyName',
);

// Запрос
$query = new WP_Query( $args );

Это возвращает что-то вроде:

пример результата запроса

Как видите, записи не содержат никаких мета-данных. Возможно ли включить мета-данные в возвращаемый массив?

PS: Я не хочу использовать дополнительные wp_queries из соображений производительности.

1
Комментарии

Стандартный WP_Query не возвращает метаданные записей. У вас есть только следующие варианты: 1) использовать get_post_meta для отдельных ключей, 2) использовать get_post_custom чтобы получить все пользовательские поля записи сразу, или 3) создать собственный запрос, используя класс $wpdb (get_results()), чтобы построить свой собственный возвращаемый объект. (Документация по классу $wpdb: http://codex.wordpress.org/Class_Reference/wpdb)

BODA82 BODA82
12 дек. 2014 г. 06:28:40
Все ответы на вопрос 5
5
26

По умолчанию WP_Query возвращает стандартные объекты WP_Post для запрашиваемых записей. Я считаю, что с помощью хитрых перезаписей и использования фильтров, доступных в WP_Query, можно добавить объекты в возвращаемый массив объектов WP_Post.

Будет ли это производительно? На мой взгляд, это скорее снизит производительность, так как вам придется объединять результаты в запросе, поскольку пользовательские поля хранятся не в таблице wp_posts, а в таблице wp_postmeta.

Получение метаданных записи происходит очень быстро и не требует дополнительных экземпляров WP_Query. Вы можете просто получить пользовательское поле с помощью функции get_post_meta(). Разработчики WordPress продумали этот момент при внедрении пользовательских полей. Они добавили кеш для них, поэтому независимо от того, запрашиваете ли вы 1 или 100 пользовательских полей, вы обращаетесь к базе данных только один раз, что очень быстро. Для полного теста и объяснения смотрите эту статью, которую я недавно написал на эту тему.

На мой взгляд, дополнительный запрос к базе данных и потраченное время того стоят и работают быстрее, чем переписывание WP_Query таким образом, чтобы включить пользовательские поля в стандартный объект записи, возвращаемый $posts.

12 дек. 2014 г. 06:37:59
Комментарии

OK, спасибо, я приму этот ответ, но честно говоря, вызывать get_post_meta() для каждого поста — это слишком муторно.. Лучше бы был способ хранить дополнительные данные либо прямо в таблице wp_posts, либо в связанной таблице, которая не так запутывает, как wp_postsmeta.

YemSalat YemSalat
12 дек. 2014 г. 06:57:01

Честно говоря, неважно, вызываете ли вы get_post_meta() или получаете эти данные как часть объекта поста — вам всё равно придётся вызывать это для каждого поста. То же самое с тегами шаблонов, такими как the_content() — их нужно вызывать для каждого поста.

Pieter Goosen Pieter Goosen
12 дек. 2014 г. 07:02:54

Значит ли это, что если нужно показать 120 постов, на странице будет выполнено 120 дополнительных запросов?

chifliiiii chifliiiii
14 сент. 2015 г. 22:25:00

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

Pieter Goosen Pieter Goosen
14 сент. 2015 г. 22:27:53

Вы правы. Я имел в виду post_thumbnails, но недавно обнаружил функцию update_post_thumbnail_cache( $the_query ). В любом случае, спасибо за разъяснение

chifliiiii chifliiiii
14 сент. 2015 г. 22:50:04
1

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

Поэтому я создал следующий SQL-запрос, которым часто пользуюсь. Надеюсь, он поможет кому-то ещё. Постараюсь объяснить его как можно лучше.

        global $wpdb;
        $pt = 'clients'; // Тип записи
        $mk = 'trainerid'; // Мета-ключ для поиска по значению
        $mv = $pid; // Искомое значение мета-ключа
        $mk1 = 'email'; // Дополнительные мета-ключи
        $mk2 = 'phone';
        $mk3 = 'gender';
        $mk4 = 'dob';
        $mk5 = 'photo';
        $mk6 = 'registrationts';
        $mk7 = 'activationts';
        $ord = 'p.post_name ASC'; // Сортировка

        $sql = "
        SELECT p.ID, p.post_title AS fullname, pm1.meta_value AS email, pm2.meta_value AS phone, pm3.meta_value AS gender, pm4.meta_value AS dob, pm5.meta_value AS photo, pm6.meta_value AS regts, pm7.meta_value AS actemailts
        FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
            AND pm.meta_key = '{$mk}'
            LEFT JOIN {$wpdb->postmeta} pm1 ON pm1.post_id = p.ID
            AND pm1.meta_key = '{$mk1}'
            LEFT JOIN {$wpdb->postmeta} pm2 ON pm2.post_id = p.ID
            AND pm2.meta_key = '{$mk2}'
            LEFT JOIN {$wpdb->postmeta} pm3 ON pm3.post_id = p.ID
            AND pm3.meta_key = '{$mk3}'
            LEFT JOIN {$wpdb->postmeta} pm4 ON pm4.post_id = p.ID
            AND pm4.meta_key = '{$mk4}'
            LEFT JOIN {$wpdb->postmeta} pm5 ON pm5.post_id = p.ID
            AND pm5.meta_key = '{$mk5}'
            LEFT JOIN {$wpdb->postmeta} pm6 ON pm6.post_id = p.ID
            AND pm6.meta_key = '{$mk6}'
            LEFT JOIN {$wpdb->postmeta} pm7 ON pm7.post_id = p.ID
            AND pm7.meta_key = '{$mk7}'
            WHERE pm.meta_value = '{$mv}'
            AND p.post_type = '{$pt}'
            AND p.post_status NOT IN ('draft','auto-draft')
            ORDER BY {$ord}
        ";

        $clients = $wpdb->get_results( $wpdb->prepare( $sql ), OBJECT );

Сначала я получаю доступ к функциям базы данных WordPress через global $wpdb.
Затем устанавливаю тип записи в переменной $pt.

Чтобы получить нужную запись, соответствующую определённому значению в метаданных, я указываю мета-ключ в $mk.

Затем задаю значение мета-ключа в переменной $mv (в данном случае значение соответствует ID записи).

Переменные $mk1$mk7 содержат дополнительные мета-ключи, которые мне нужны (их значения я получу в SELECT-запросе).

Также я выношу параметр сортировки в переменную $ord.

SELECT-запрос работает следующим образом:
Я выбираю ID записи и её заголовок из таблицы {$wpdb->posts} (псевдоним p).

Затем выбираю все необходимые метаданные, получая их через pm1pm7 и переименовывая (AS) для удобства при получении данных из объекта.

Создаю LEFT JOIN для метаданных, по которым нужно искать совпадение (pm).

Создаю 7 LEFT JOIN'ов для каждого из метаданных, которые нужно получить (pm1pm7).

Условие WHERE основано на первом LEFT JOIN (pm), чтобы получить только записи, где метаданные совпадают.

Добавляю условие AND для типа записи и для статусов, которые не являются черновиками (только опубликованные записи).

В конце добавляю сортировку ORDER BY.

Этот запрос работает быстро и использует встроенные индексы WordPress, поэтому выглядит эффективным.

Не знаю, есть ли что-то лучше этого, но если есть — с радостью воспользуюсь.

Надеюсь, это поможет.

Маркус

14 февр. 2017 г. 03:21:29
Комментарии

Спасибо, этот пост очень полезен. Я создал представление со всеми необходимыми метаполями, и теперь получать любые данные стало очень быстро и просто

Liko Liko
23 нояб. 2017 г. 14:01:22
1

Этот вопрос был задан более года назад, но у меня такая же проблема, и вот функция, которая добавит каждое meta_value и meta_key в объект $wp_query.

Вместо запроса метаданных каждого поста в цикле while, эта функция выполнит один дополнительный запрос, например:

"SELECT meta_key, meta_value, post_id FROM $wpdb->postmeta WHERE post_id IN (1,2,3,4,5...)"

где (1,2,3,4,5...) — это текущие ID запрашиваемых постов из $wp_query

if(!function_exists('add_query_meta')) {
  function add_query_meta($wp_query = "") {

      // Возвращаем, если wp_query пуст или postmeta уже существует
      if( (empty($wp_query)) || (!empty($wp_query) && !empty($wp_query->posts) && isset($wp_query->posts[0]->postmeta)) ) { return $wp_query; }

      $sql = $postmeta = '';
      $post_ids = array();
      $post_ids = wp_list_pluck( $wp_query->posts, 'ID' );
      if(!empty($post_ids)) {
        global $wpdb;
        $post_ids = implode(',', $post_ids);
        $sql = "SELECT meta_key, meta_value, post_id FROM $wpdb->postmeta WHERE post_id IN ($post_ids)";
        $postmeta = $wpdb->get_results($sql, OBJECT);
        if(!empty($postmeta)) {
          foreach($wp_query->posts as $pKey => $pVal) {
            $wp_query->posts[$pKey]->postmeta = new StdClass();
            foreach($postmeta as $mKey => $mVal) {
              if($postmeta[$mKey]->post_id == $wp_query->posts[$pKey]->ID) {
                $newmeta[$mKey] = new stdClass();
                $newmeta[$mKey]->meta_key = $postmeta[$mKey]->meta_key;
                $newmeta[$mKey]->meta_value = maybe_unserialize($postmeta[$mKey]->meta_value);
                $wp_query->posts[$pKey]->postmeta = (object) array_merge((array) $wp_query->posts[$pKey]->postmeta, (array) $newmeta);
                unset($newmeta);
              }
            }
          }
        }
        unset($post_ids); unset($sql); unset($postmeta);
      }
      return $wp_query;
  }
}

Дополнительные "postmeta" будут записаны для каждого $wp_query->posts[$i]

$wp_query->posts[0]->postmeta

Пример с 'someMetaKeyName' — не забудьте добавить add_query_meta() в function.php вашей темы.

$args = array (
    'post_type' => 'page',
    'meta_key' => 'someMetaKeyName',
);

// Запрос
$query = new WP_Query( $args );
if($wp_query->have_posts()) {
  $wp_query = add_query_meta($wp_query);
    $i = 0;
    while($wp_query->have_posts()) {
      $wp_query->the_post();
      $post_id = get_the_id();

      // Получаем $someMetaKeyName в текущем посте
      foreach($wp_query->posts[$i]->postmeta as $k => $v) {
        switch($v->meta_key) {
          case('someMetaKeyName') : {
            $someMetaKeyName = $v->meta_value;
            break;
          }
        }
      }

      // Ваш код здесь
      // Пример 
      echo isset($someMetaKeyName) ? '<h3>'.$someMetaKeyName.'</h3>' : '';


      $i++;
    }
}
5 мая 2016 г. 16:10:27
Комментарии

Мне нравится это решение.

Armstrongest Armstrongest
1 авг. 2017 г. 18:29:58
1

Мне просто нужно было получить одно дополнительное значение post_meta (в моем случае шаблон), и я смог добиться чего-то похожего на то, что вам нужно, используя get_pages с параметром meta_key.

$args = [
  'parent' => $post_id, // в моем примере ищем дочерние записи определенного поста
  'meta_key' => '_wp_page_template', // ищем шаблон, используемый для поста
];

$posts = get_pages($args);

Это возвращает массив объектов постов в таком виде:

Array
(
  [0] => WP_Post Object
    (
      [ID] => 22765
      [post_author] => 173
      [post_date] => 2022-03-14 21:11:18
      ...
      [meta_key] => _wp_page_template
      [meta_value] => template-module-content.php // <-- ВОТ ОНО!
      ...
    )

Затем вы можете получить доступ к meta_value следующим образом:

echo $posts[0]->meta_value;

// возвращает 'template-module-content.php' в моем случае

Признаю, что это может быть узкоспециализированный случай, но это именно то, что мне было нужно

16 мар. 2022 г. 04:39:25
Комментарии

Похоже, что в WordPress 6.3 этот небольшой трюк перестал работать

Todd Todd
10 авг. 2023 г. 19:20:17
3
-1

Эй, попробуй этот вариант, думаю, он будет работать нормально.

$args = array(
            'post_type' => 'page',
            'meta_key' => 'someMetaKeyName',
            'meta_query' => array(
                array(
                        'key' => 'someMetaKeyName',
                        'type' => 'CHAR',
                   ),
                ),
        );

    $query = new WP_Query( $args );
12 дек. 2014 г. 05:44:43
Комментарии

Какова причина, по которой вы использовали meta_key и meta_query[]['key'] одновременно?

kaiser kaiser
12 дек. 2014 г. 05:56:06

Нет, это не работает и возвращает массив записей без связанных мета-данных.

YemSalat YemSalat
12 дек. 2014 г. 05:57:53

meta_key и/или meta_query не изменяют тип возвращаемых результатов, только сам запрос.

BODA82 BODA82
12 дек. 2014 г. 06:39:20