20

I would like to create a wp_query that would return posts meta inside the posts array.

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

// The Query
$query = new WP_Query( $args );

This returns something like:

enter image description here

As you can see the posts do not have any meta data, is it possible to include the meta data in the returned array as well?

PS I don't want additional wp_queries for performance reasons.

  • 2
    The standard WP_Query does not return a posts meta data. The only options you have are to: 1) run get_post_meta on individual keys, 2) run get_post_custom to get all of a posts custom fields in one shot, or 3) create your own query using the $wpdb class (get_results()) to build your own return object. ($wpdb class documentation: codex.wordpress.org/Class_Reference/wpdb) – BODA82 Dec 12 '14 at 4:28
20

By default, WP_Query returns the standard WP_Post objects for the posts being queried. I believe with some clever rewrite and use of the filters given in WP_Query you can add objects to the returned WP_Post objects array.

Will this be performant? In my opinion, it will hurt performance more as you will need to join results in your query as custom fields are not saved in the wp_posts table, but in the wp_postmeta table

Retrieving post meta is really fast and it does not require any extra instance of WP_Query. You can simply call the custom field with get_post_meta(). WordPress was very thoughtful when the custom fields was introduced. They added a cache to cache them, so whether you are querying 1 or 100 custom fields, you are hitting the database once, superfast. For a complete test and explanation, see this post I have recently done on this subject.

In my opinion, the extra database call and the actual time spent is worth it and faster than rewriting WP_Query in such a way to include custom fields in the standard post object returned by $posts

  • OK, thanks, I'll pick this one as accepted, but to be honest its just so much hassle calling get_post_meta() for every single post.. I'd rather there was a way to store additional data either directly in the wp_posts table, or in a related table which is not as much of a mindf*ck as wp_postsmeta is. – YemSalat Dec 12 '14 at 4:57
  • Well, to be honest, whether calling get_post_meta() or it as a post object, you will need to call it on every post. It is the same with template tags like the_content(), you have to call it on every post. – Pieter Goosen Dec 12 '14 at 5:02
  • 2
    This means that if you have to show 120 posts you will have an extra 120 queries in your page? – chifliiiii Sep 14 '15 at 19:25
  • All postdata get saved in an cache, so you will not have extra queries when cal;ling post meta – Pieter Goosen Sep 14 '15 at 19:27
  • You are right. I was referring to post_thumbnails but I recently found update_post_thumbnail_cache( $the_query ). Thanks anyway for the clarification – chifliiiii Sep 14 '15 at 19:50
4

I had a similar problem recently, I needed to get 7 pieces of metadata from a custom post type, but also needed to get the post based on a piece of metadata.

So I created the following SQL statement, I use it often. Hopefully it will help someone else. I'll try and explain it as best I can.

        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 );

First I get the wordpress database functions with global $wpdb. Then I set the posttype with $pt. To get the correct post that matches a specific value in post_meta, I set the $mk (meta_key)

Then I set the $mv (meta_value) var. (in this case the meta value matches a postid)

$mk1-$mk7 are the meta_keys I want from each post. (I'll grab the values in the select statement)

I also make the 'order by' a var, by setting $ord

The select statement goes as follows: I select the post ID and the post_title from the POST or 'p.'

Then I select all the metadata I need selecting them with pm1. -> pm.7 and grabbing the meta_value and renaming them ( AS ) so it's more readable when retrieving the data from my object.

I create a LEFT JOIN for the meta data I need to match to the post. (pm)

I create 7 left join's for each of the meta data I need to retrieve. (pm1-pm7)

The WHERE statement is based on the first LEFT JOIN (pm) so that it will know I need only the posts where the metadata matches.

I also add an 'AND' for the post type, and for the post_statuses that are not drafts. (so only published posts)

Finally I add the 'order by' clause.

This works fast and with the built-in indexes in Wordpress, so it seems efficient.

Don't know if something is better than this, but if it is, I'd love to use it.

Hope this helps.

Marcus

  • Thanks, this post is very helpful. I created a view with all meta fields i need and now is very fast and easy to get any data i want – Liko Nov 23 '17 at 12:01
3

This question is more than 1 year old, but I have the same problomlem, and here is function that will add each meta_value and meta_key to $wp_query object,

instead of query each post meta in while loop, this function will do One extra query example:

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

where (1,2,3,4,5...) is curently quered post IDs from $wp_query

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

      //return In case if wp_query is empty or postmeta already exist
      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;
  }
}

Additioanal "postmeta" will be written to each $wp_query->posts[$i]

$wp_query->posts[0]->postmeta

Example with 'someMetaKeyName' don`t forget to put

add_query_meta() to your theme functin.php

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

// The Query
$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();

      //Get $someMetaKeyName in current post
      foreach($wp_query->posts[$i]->postmeta as $k => $v) {
        switch($v->meta_key) {
          case('someMetaKeyName') : {
            $someMetaKeyName = $v->meta_value;
            break;
          }
        }
      }

      //Your Code here
      //Example 
      echo isset($someMetaKeyName) ? '<h3>'.$someMetaKeyName.'</h3>' : '';


      $i++;
    }
}
  • I love this solution. – Armstrongest Aug 1 '17 at 15:29
0

Hey Please try this one I think it work fine.

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

    $query = new WP_Query( $args );
  • What's the reason that you used meta_key and meta_query[]['key'] as well? – kaiser Dec 12 '14 at 3:56
  • 1
    No, this does not work and brings back the array of posts without meta associated with them. – YemSalat Dec 12 '14 at 3:57
  • 3
    meta_key and/or meta_query do not modify the type of results returned, only the query itself. – BODA82 Dec 12 '14 at 4:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.