9

I'm trying to get only the fields that I need using the get_posts() function in WordPress. I currently have the following code:

    $posts_args = array(
        'orderby' => 'post_date',
        'order' => 'DESC',
        'post_type' => 'post',
        'post_status' => 'publish',
        'posts_per_page' => 5,
        'fields' => 'ids'
    );

    $post_ids_r = get_posts($posts_args);

This works fine if I only want to get the id. But if I want to get the permalink or the title of the post along with the ids that's where I'm not sure what to do. I already tried the following:

'fields' => array('ids', 'post_titles')
'fields' => 'ids,post_titles'
'fields' => 'ids,titles'
'fields' => array('ids','titles')

But nothing works, I guess the only one that it recognizes is the ids field. Is there any other way to do this if its really not possible to do it using get_posts()? Thanks in advance.

5

get_posts passes the heavy lifting off to WP_Query and if you look at the source of that class you can see that there are only a limited number of options with that fields argument. There are only three options in that switch-- ids, id=>parent, and the default case, everything.

You can use the posts_fields filter to alter what fields get returned, though it looks like you need to pass 'suppress_filters => false in the arguments in order to get that filter to run. It should look something like this:

function alter_fields_wpse_108288($fields) {
  return 'ID,post_title'; // etc
}
add_filter('posts_fields','alter_fields_wpse_10888');

However, there is a larger problem. The post objects that get returned are created by a call to get_post and it doesn't honor the values passed into the original query and I don't see a way to change what gets returned either in get_posts or in the WP_Post class itself.

1
  • 4
    It seems like the 'fields' parameter has been introduced in a 5.x version of WP. Just doing get_posts(array('fields' => 'ids')) would get posts with just the post ids without doing the posts_fields filter. Please correct me if I am wrong, because it worked for me in the latest wp version (5.2.4) without adding the filter. Thanks!
    – AhmadKarim
    Oct 25 '19 at 15:12
5

Have a look to this

function get_posts_fields( $args = array() ) {
  $valid_fields = array(
    'ID'=>'%d', 'post_author'=>'%d',
    'post_type'=>'%s', 'post_mime_type'=>'%s',
    'post_title'=>false, 'post_name'=>'%s', 
    'post_date'=>'%s', 'post_modified'=>'%s',
    'menu_order'=>'%d', 'post_parent'=>'%d', 
    'post_excerpt'=>false, 'post_content'=>false,
    'post_status'=>'%s', 'comment_status'=>false, 'ping_status'=>false,
    'to_ping'=>false, 'pinged'=>false, 'comment_count'=>'%d'
  );
  $defaults = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'orderby' => 'post_date',
    'order' => 'DESC',
    'posts_per_page' => get_option('posts_per_page'),
  );
  global $wpdb;
  $args = wp_parse_args($args, $defaults);
  $where = "";
  foreach ( $valid_fields as $field => $can_query ) {
    if ( isset($args[$field]) && $can_query ) {
      if ( $where != "" )  $where .= " AND ";
      $where .= $wpdb->prepare( $field . " = " . $can_query, $args[$field] );
    }
  }
  if ( isset($args['search']) && is_string($args['search']) ) {
      if ( $where != "" )  $where .= " AND ";
      $where .= $wpdb->prepare("post_title LIKE %s", "%" . $args['search'] . "%");
  }
  if ( isset($args['include']) ) {
     if ( is_string($args['include']) ) $args['include'] = explode(',', $args['include']); 
     if ( is_array($args['include']) ) {
      $args['include'] = array_map('intval', $args['include']); 
      if ( $where != "" )  $where .= " OR ";
      $where .= "ID IN (" . implode(',', $args['include'] ). ")";
    }
  }
  if ( isset($args['exclude']) ) {
     if ( is_string($args['exclude']) ) $args['exclude'] = explode(',', $args['exclude']); 
     if ( is_array($args['exclude']) ) {
      $args['exclude'] = array_map('intval', $args['exclude']);
      if ( $where != "" ) $where .= " AND "; 
      $where .= "ID NOT IN (" . implode(',', $args['exclude'] ). ")";
    }
  }
  extract($args);
  $iscol = false;
  if ( isset($fields) ) { 
    if ( is_string($fields) ) $fields = explode(',', $fields);
    if ( is_array($fields) ) {
      $fields = array_intersect($fields, array_keys($valid_fields)); 
      if( count($fields) == 1 ) $iscol = true;
      $fields = implode(',', $fields);
    }
  }
  if ( empty($fields) ) $fields = '*';
  if ( ! in_array($orderby, $valid_fields) ) $orderby = 'post_date';
  if ( ! in_array( strtoupper($order), array('ASC','DESC')) ) $order = 'DESC';
  if ( ! intval($posts_per_page) && $posts_per_page != -1)
     $posts_per_page = $defaults['posts_per_page'];
  if ( $where == "" ) $where = "1";
  $q = "SELECT $fields FROM $wpdb->posts WHERE " . $where;
  $q .= " ORDER BY $orderby $order";
  if ( $posts_per_page != -1) $q .= " LIMIT $posts_per_page";
  return $iscol ? $wpdb->get_col($q) : $wpdb->get_results($q);
}

It's a function that mimics get_posts but with the ability to get the fields you desire. Be aware: this function is not get_posts and has 2 great limitations: run only in posts table so taxonomy and meta query cannot be run!

However, the query can rely on all the post fields and on some 'special' arguments like include, exclude and search.

The good part is this: the fields you are able to retrieve are all the field of the post table. Just pass a list or an array in the fields argument. Bonus: passing only one field is returned a one dimensional array of strings or integers (instead of an array of objects).

List of Available args are:

$available_args = array(
  'ID', // int
  'post_author', // string
  'post_type', // string
  'post_mime_type', // string
  'post_name', // string
  'post_date', // string
  'post_modified', // string
  'menu_order', // int
  'post_parent', // int 
  'post_status', // string
  'comment_status', // string
  'comment_count', // int 
  'orderby', // string, a valid field name
  'order', // string 'ASC', or 'DESC',
  'posts_per_page', // int
  'include', // array (or comma separed string) of post ids
  'exclude', // array (or comma separed string) of post ids
  'search', // string if passed will search for it in post title
  'fields', // array (or comma separed string) of fields to retrieve.
            // If only 1 field is passed a 'flat' array is returned 
);

Examples of usage

// Retrieve the date and the title of pages having 'Hello' in the title
$pages_hello = get_posts_fields("post_type=page&search=Hello&fields=post_date,post_title");


// another example
$args = array(
  'post_type' => 'custom_post',
  'posts_per_page' => -1,
  'post_parent' => 1,
  'include' => array(2,3,4),
  'exclude' => '6,8,10',
  'fields' => array('post_title', 'comment_status')
);
get_posts_fields($args);


// One more, just for fun ;)
$args = array(
  'post_type' => 'attachment', 'posts_per_page' => -1,
  'post_status' => 'inherit', 'fields' => 'post_mime_type'
);
foreach ( array_count_values ( get_posts_fields($args) ) as $mime => $count ) {
  echo "I have $count media of the type $mime" . PHP_EOL;
}
2

You can only use 'ids' or 'id=>parent' for the parameter fields.

If you parse something else it will return all fields (this is default).

However, it would be nice if Wordpress could add the following 2 options: 'titles' and 'ids_and_titles'.

I am not aware of a way to parse an array for this parameter. I also think it will never happen, since the limits of the answer given by G. M.

More info: http://codex.wordpress.org/Class_Reference/WP_Query#Return_Fields_Parameter

1
  • 1
    id=>parent is no valid argument.
    – kaiser
    Jan 25 '14 at 18:47
1

Thanks gmazzap for that function! I was looking for a solution to get some custom fields in one with the get_posts, so I felt free to extend it for custom fields of the postmeta table:

function get_posts_fields($args = array())
{
    $valid_fields = array(
        'ID' => '%d',
        'post_author' => '%d',
        'post_type' => '%s',
        'post_mime_type' => '%s',
        'post_title' => false,
        'post_name' => '%s',
        'post_date' => '%s',
        'post_modified' => '%s',
        'menu_order' => '%d',
        'post_parent' => '%d',
        'post_excerpt' => false,
        'post_content' => false,
        'post_status' => '%s',
        'comment_status' => false,
        'ping_status' => false,
        'to_ping' => false,
        'pinged' => false,
        'comment_count' => '%d',
        'custom_fields' => false,
    );

    $defaults = array(
        'post_type' => 'post',
        'post_status' => 'publish',
        'orderby' => 'post_date',
        'order' => 'DESC',
        'posts_per_page' => get_option('posts_per_page')
    );

    global $wpdb;

    $args = wp_parse_args($args, $defaults);
    $where = "";
    $meta = "";
    $groupBy = "";

    foreach ($valid_fields as $field => $can_query) {
        if (isset($args[$field]) && $can_query) {
            if ($where != "") {
                $where .= " AND ";
            }
            $where .= $wpdb->prepare(
                $wpdb->posts.".".$field . " = " . $can_query,
                $args[$field]
            );
        }
    }

    if (isset($args['search']) && is_string($args['search'])) {
        if ($where != "") {
            $where .= " AND ";
        }
        $where .= $wpdb->prepare(
            "post_title LIKE %s",
            "%" . $args['search'] . "%"
        );
    }

    if (isset($args['include'])) {
        if (is_string($args['include'])) {
            $args['include'] = explode(',', $args['include']);
        }
        if (is_array($args['include'])) {
            $args['include'] = array_map('intval', $args['include']);
            if ($where != "") {
                $where .= " OR ";
            }
            $where .= $wpdb->posts.".ID IN (" . implode(',', $args['include']) . ")";
        }
    }

    if (isset($args['exclude'])) {
        if (is_string($args['exclude'])) {
            $args['exclude'] = explode(',', $args['exclude']);
        }
        if (is_array($args['exclude'])) {
            $args['exclude'] = array_map('intval', $args['exclude']);
            if ($where != "") {
                $where .= " AND ";
            }
            $where .= $wpdb->posts.".ID NOT IN (" . implode(',', $args['exclude']) . ")";
        }
    }




    extract($args);
    $iscol = false;
    if (isset($fields)) {
        if (is_string($fields)) {
            $fields = explode(',', $fields);
        }
        if (is_array($fields)) {
            $fields = array_intersect($fields, array_keys($valid_fields));
            if (count($fields) == 1) {
                $iscol = true;
            }

            for($i = 0; $i < count($fields); $i++) {
              $fields[$i] = "$wpdb->posts.$fields[$i]";
            }

        }
    }

    if (isset($args['custom_fields'])) {
      if (is_string($args['custom_fields'])) {
            $args['custom_fields'] = explode(',', $args['custom_fields']);
      }


      if (is_array($args['custom_fields'])) {
          foreach( $args['custom_fields'] as $custom_field) {
            $fields[] =  "MAX(CASE WHEN df_postmeta.meta_key = '$custom_field' then df_postmeta.meta_value ELSE NULL END) as $custom_field";
          }
          $meta = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->postmeta.post_id = $wpdb->posts.ID)";
          $groupBy = " GROUP BY $wpdb->posts.ID, $wpdb->posts.post_title";
        }
    }

    if (empty($fields)) {
        $fields = '*';
    } else {
      $fields = implode(',', $fields);
    }



    if( in_array($orderby, $valid_fields) ) {
      $orderby = $wpdb->posts.'.'.$orderby;
    }else if( isset( $args['custom_fields']) && in_array($orderby, $args['custom_fields']) ) {
      $orderby = $orderby;
    } else {
      $orderby = $wpdb->posts.'.post_date';
    }


    if (!in_array(strtoupper($order), array('ASC', 'DESC'))) {
        $order = 'DESC';
    }

    if (!intval($posts_per_page) && $posts_per_page != -1) {
        $posts_per_page = $defaults['posts_per_page'];
    }

    if ($where == "") {
        $where = "1";
    }



    $q = "SELECT $fields FROM $wpdb->posts " . $meta . " WHERE " . $where;
    $q .= $groupBy;
    $q .= " ORDER BY $orderby $order";

    if ($posts_per_page != -1) {
        $q .= " LIMIT $posts_per_page";
    }

    print $q;

    return $iscol ? $wpdb->get_col($q) : $wpdb->get_results($q);
}

You can use the orderby of that fields too. A small example:

$args = array(
    'post_type' => 'projects',
    'posts_per_page' => -1,
    'fields' => array('ID', 'post_title'),
    'orderby' => 'project_clients',
    'order' => 'DESC',

    'custom_fields' => array(
        'project_clients',
        'project_dop'
     ),
);
$projects = get_posts_fields($args);
0

If you want to display the post title inside a template or in a plugin, you can use:

get_the_title($ID)

See wordpress reference: http://codex.wordpress.org/Function_Reference/get_the_title

If you're using it outside the loop, the $ID is required. Either way, it will probably be used like this:

You can also use:

the_permalink() - to retrieve post permalink the_title() - to retrieve post title

These are all wordpress template tags. A full list of template tags you can use are found here: http://codex.wordpress.org/Template_Tags

1
  • How does this answer the question?
    – anastymous
    Oct 10 '19 at 1:41
0

Interestingly enough, you can do this with the WP Rest API using the _fields parameter

https://yoursite.com/wp-json/wp/v2/posts?_fields=author,id,excerpt,title,link

More info on the API here: https://developer.wordpress.org/rest-api/

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.