1. Create a query to retrieve the posts from the database.
2. Use a loop to iterate through the results of the query and create an array of post objects.
3. Use the PHP function array_map() to apply a function to each post object in the array. This function should create a list item for each post.
4. Use the PHP function implode() to join the list items together into a single string.
5. Output the string to the page.
WordPress get_posts
is a powerful function allowing developers to retrieve pieces of content from the WordPress database. You can specify in the finest detail which posts, pages, and custom post types you’re looking for, get your custom result set, then filter and order the items like a PHP/MySQL ninja.
But don’t be scared if you’re not a PHP professional, there are countless PHP tutorials you can watch or read and learn the language. You just need a little knowledge of PHP to create custom lists of posts to display on your website as the get_posts
function keeps an array of parameters allowing to build simple or advanced queries.
Using WordPress get_posts
is a two-step process:
- First, you have to build your custom query. Actually, it won’t look like a MySQL query, and you won’t write any
SELECT
statement. You just need to define an array of parameters and pass it to theget_posts
function. WordPress converts that array into a real and secure MySQL query, runs it against the database, and returns an array of posts. - Second, you have to traverse the result set returned by
get_posts
with a foreach cycle.
That being said, in this post, we will first dive into the key concepts mentioned above, specifically how get_posts
works, how to build a custom query, and how to show data on the front site.
Then, I will provide a real-world example with a snippet of code you can grab, edit and use on your staging environment for your tests and development.
Note: We usually differentiate between posts, pages, and custom post types. In this article, we use the term ‘posts’ for regular blog posts as well as for pages and custom post types. All these post types are stored in the ‘wp_posts’ table of the database. The main difference between post types is in the value of the ‘post_type’ field. From a developer’s perspective, posts, pages, and custom post types are all posts.
Introduction to the WordPress get_posts Function
The Codex describes the get_posts
function as follows:
Retrieves an array of the latest posts, or posts matching the given criteria.
We can use get_posts
this way:
$args = array(
'numberposts' => 20,
'category' => 4
);
$my_posts = get_posts( $args );
if( ! empty( $my_posts ) ){
$output="<ul>";
foreach ( $my_posts as $p ){
$output .= '<li><a href="' . get_permalink( $p->ID ) . '">'
. $p->post_title . '</a></li>';
}
$output .= '</ul>';
}
The function above retrieves the latest 20 blog posts in the specified category (by default the 'post_type'
is 'post'
) and returns an array of $post
objects. You can iterate over the array to display the posts on the screen. It’s pretty easy, right?
get_posts
uses WP_Query
to retrieve post items, and it keeps an array of the same parameters available in WP_Query
(with few exceptions). So we have a huge list of variables we can use to build our custom queries. These parameters are grouped in the following 15 categories:
- Author Parameters
- Category Parameters
- Tag Parameters
- Taxonomy Parameters
- Search Parameters
- Post & Page Parameters
- Password Parameters
- Post Type Parameters
- Order & Orderby Parameters
- Date Parameters
- Custom Field (post meta) Parameters
- Permission Parameters
- Mime Type Parameters
- Caching Parameters
- Return Fields Parameter
A quick look at the list above can give you an idea of the variety of the custom queries you can build and run against the WordPress database. So, let’s dive deeper into query parameters and start to build our lists of posts.
How to Build Queries With WordPress get_posts
Each category of parameters relates to the same piece of information. For example, we can build a query to retrieve posts from the specified author(s) or excluding the specified author(s), defining the author by ID or nicename. The same way, we can build queries fetching posts by category, tag, taxonomy, date, custom fields and even more.
How To Use Parameters to Build Simple Queries
Many parameters can be used in a quite similar way, regardless of the category they belong to. For example, the following parameters allow to query the database by post author(s):
author
(int) – author IDauthor_name
(string) – author’suser_nicename
author__in
(array) – an array of multiple authors’ IDsauthor__not_in
(array) – an array of multiple authors’ IDs to be excluded from the result set
How can we use these parameters?
In the following example, the parameter 'author'
specifies that we want the most recent blog posts written by the author with ID = 1:
$my_posts = get_posts( array( 'author' => 1 ) );
The same ‘author’ parameter allows to query the database in different ways:
// return an array of posts from specific authors
$my_posts = get_posts( array( 'author' => '1,5,12' ) );
// return an array of posts excluding the specified author
$my_posts = get_posts( array( 'author' => -1 ) );
So, depending on the parameter’s value, you’ll have a result set with posts from a single author (integer), from multiple authors (a list of comma-separated values) or excluding an author (negative values).
Other parameters provide additional flexibility. For example, the following call to get_posts
returns an array of the latest blog posts from multiple authors:
// return an array of posts from multiple authors
$my_posts = get_posts( array( 'author__in' => array( 1, 5, 12 ) ) );
And we can also exclude multiple authors:
// return an array of posts from multiple authors
$my_posts = get_posts( array( 'author__not_in' => array( 1, 5, 12 ) ) );
Similarly, we can use category params, tag params, post type params, with some specific differences. See, as an example, category params:
cat
(int)category_name
(string)category__and
(array)category__in
(array)category__not_in
(array)
Anyway, not all parameters are as easy to use as these parameters. Additionally, we can use category params, post type params, mime type params, etc. all in a single query. This means that we have granular control over the items in the result set, and we can build more advanced queries based on post types, custom taxonomies and custom fields altogether.
So, let’s dive deeper!
How To Build Advanced Queries in WordPress
Let’s move a step forward with a more advanced query based on custom post types and custom taxonomies. Say you have the following post type:
name: book
taxonomy name: book_category, book_author
support for: title, editor, thumbnail, excerpt, custom-fields
Custom Post Types and Custom Taxonomies
Suppose you want a list of the most recent books in the specified book_category
custom taxonomy. Here is the array of arguments:
$args = array(
'post_type' => 'book',
'tax_query' => array(
array(
'taxonomy' => 'book_category',
'field' => 'slug',
'terms' => 'sci-fi'
)
),
);
The arguments above simply tell WordPress to retrieve all books in 'sci-fi'
'book_category'
.
The 'tax_query'
parameter takes an array of argument arrays (i.e. an array of arrays). These nested arrays allow to build very complex queries based on multiple taxonomies, as shown in the example below:
$args = array(
'numberposts' => 10,
'post_type' => 'book',
'relation' => 'AND',
'tax_query' => array(
array(
'taxonomy' => 'book_category',
'field' => 'slug',
'terms' => 'sci-fi'
),
array(
'taxonomy' => 'book_author',
'field' => 'term_id',
'terms' => 22
)
)
);
These parameters allow us to retrieve a list of the latest 10 'book'
post types in the 'sci-fi'
'book_category'
, written by the 'book_author'
with ID #22. The 'relation'
parameter sets the logical relationship between each taxonomy listed in 'tax_query'
. Above we set its value to AND
because we need to retrieve all books belonging to the 'sci-fi'
category AND
written by author #22.
How to Build Meta Queries Using Custom Field Parameters
Occasionally, you may need to build lists of posts based on a specific custom field key and/or value.
$args = array(
'meta_key' => 'cover',
'meta_value' => 'paperback',
'meta_compare' => '='
);
These parameters allow us to retrieve all posts by custom field key and value. 'meta_compare'
sets the operator required to test the value of the 'meta_value'
parameter. Here 'meta_value'
is '='
, which is also the default value.
Available values are '='
, '!='
, '>'
, '>='
, '<'
, '<='
, 'LIKE'
, 'NOT LIKE'
, 'IN'
, 'NOT IN'
, 'BETWEEN'
, 'NOT BETWEEN'
, 'NOT EXISTS'
, 'REGEXP'
, 'NOT REGEXP'
or 'RLIKE'
.
This is a pretty easy example, but we can build more advanced queries. In the next example, we query the database for fantasy books published after 2010:
$args = array(
'post_type' => 'book',
'meta_key' => 'year_published',
'meta_value_num' => 2010,
'meta_compare' => '>',
'tax_query' => array(
array(
'taxonomy' => 'book_category',
'field' => 'slug'
'terms' => 'fantasy'
)
)
);
And we can go even further. In the next example we are mixing up a post type with a custom taxonomy and two custom fields:
$args = array(
'post_type' => 'book',
'tax_query' => array(
array(
'taxonomy' => 'book_category',
'field' => 'slug'
'terms' => array( 'fantasy' )
)
),
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'year_published',
'value' => 2010,
'type' => 'numeric',
'compare' => '>',
),
array(
'key' => 'price',
'value' => array( 10, 25 ),
'type' => 'numeric',
'compare' => 'BETWEEN',
)
)
);
Here we set an array of parameters to retrieve a list of fantasy books published after 2010 which cost BETWEEN
$10 and $25.
You can see that the 'meta_query'
parameter works much like the 'tax_query'
parameter. It keeps an array of arrays, allowing us to buind advanced queries based on multiple meta key/value pairs. For a comprehensive list of query parameters and a good number of examples, see the WP_Query
documentation.
Why get_posts is Limited to 5 WordPress Posts?
The get_posts
function takes the same arguments as WP_Query::parse_query()
(see the Codex), but some specific parameters make it work slightly differently from a WP_Query
object.
Maybe you didn’t use the 'numberposts'
parameter in your queries and you’re wondering why you see just 5 items in your list.
By default, the number of posts you set in Settings → Reading admin page determines the number of posts to be retrieved by a WordPress query. Anyway, if you don’t specify a custom value for 'numberposts'
or 'posts_per_page'
, get_posts
returns a different number of posts.
'numberposts'
is the total number of posts to retrieve. It is an alias of'posts_per_page'
inWP_Query
, but there’s a difference between the two: by default, the number of posts to retrieve when usingget_posts
is 5, while'posts_per_page'
inWP_Query
defaults to the number of posts per page of your WordPress blog. You can override the default value by setting a custom value for'numberposts'
or'posts_per_page'
in the array of arguments.
In addition to 'numberposts'
, the following parameters are specific of get_posts
:
'category'
is a comma-separated list of category IDs. It is an alias of the'cat'
parameter inWP_Query
.'include'
is a comma-separated list of post IDs. This is an alias of the'post__in'
parameter inWP_Query
.'exclude'
is a comma-separated list of post IDs.'suppress_filters'
specifies whether to suppress filters. This parameter defaults totrue
inget_posts
, while it defaults tofalse
inWP_Query
(see it on Track).
The get_posts
function is defined in wp-includes/post.php
. You can deep dive into how get_posts
works by checking the source code either on Track (WordPress 5.2) or in your local WordPress installation.
Ordering Items
'orderby'
and 'order'
sort the items in the result set. You car sort posts by 'ID'
, 'author'
, 'title'
, 'name'
, 'type'
, 'date'
, 'modified'
, 'parent'
, 'rand'
, 'comment_count'
and in many other ways, in ascending or descending order.
If you have a simple query, you just need to set a value for 'order'
and 'orderby'
. In the following example, posts are sorted by post name in ascending order:
$args = array(
'author' => '1,5,12',
'orderby' => 'name',
'order' => 'ASC'
);
That’s pretty straightforward. But what if you had an advanced query? I.e: Can we sort items by one or more custom field values in an advanced meta query?
WordPress 4.0 and WordPress 4.2 brought important improvements to 'orderby'
and 'meta_query'
params. We now have a new syntax for ordering by specific clauses of a meta query. Thanks to the new syntax, we can use indexes to create references to the specific clauses of the meta query from the 'orderby'
parameter.
Thanks to these improvements, the meta query in the example above can be written as follows:
$args = array(
'meta_query' => array(
'relation' => 'AND',
'year_clause' => array(
'key' => 'year_published',
'value' => 2010,
'type' => 'numeric',
'compare' => '>',
),
'price_clause' => array(
'key' => 'price',
'value' => array( 10, 25 ),
'type' => 'numeric',
'compare' => 'BETWEEN',
)
),
'orderby' => 'price_clause',
);
In the example above we ordered elements by 'price_clause'
.
And we can do even more. As of WordPress 4.0, we can pass to get_posts
an array of meta query indexes instead of a single index, as seen in the example below:
$args = array(
'meta_query' => array(
'relation' => 'AND',
'year_clause' => array(
'key' => 'year_published',
'value' => 2010,
'type' => 'numeric',
'compare' => '>',
),
'price_clause' => array(
'key' => 'price',
'value' => array( 10, 25 ),
'type' => 'numeric',
'compare' => 'BETWEEN',
)
),
'orderby' => array( 'price_clause' => 'ASC', 'year_clause' => 'DESC' ),
);
Congratulations, you’ve built an advanced meta query, and sorted results first by 'price_clause'
in ascending order, then by 'year_clause'
in descending order.
See the full list of sorting options in the Codex.
It’s time for us to display data on the front page.
Suggested reading: How to Easily Create and Use a phpinfo Page.
How to Display get_posts Returned Data
WordPressget_posts
returns an array of WP_Post
objects giving us access to a number of variables for each selected post stored in wp_posts
database table:
- ID
- post_author
- post_name
- post_type
- post_title
- post_date
- post_date_gmt
- post_content
- post_excerpt
- post_status
- comment_status
- ping_status
- post_password
- post_parent
- post_modified
- post_modified_gmt
- comment_count
- menu_order
You can easily access these data with a foreach
cycle like the following:
$custom_posts = get_posts( $args );
if( ! empty( $custom_posts ) ){
$output="<ul>";
foreach ( $custom_posts as $p ){
$output .= '<li><a href="'
. get_permalink( $p->ID ) . '">'
. $p->post_title . '</a></li>';
}
$output .= '</ul>';
}
return $output ?? '<strong>Sorry. No posts matching your criteria!</strong>';
If get_posts
found at least one post, it returns an array of items we can traverse to show the post title and a link to the original post. We used the get_permalink
function to retrieve the post permalink, as we don’t have a corresponding WP_Post
variable.
That’s pretty easy, but how can we implement that code and build our custom lists of posts using WordPress get_posts
?
You can show lists of posts on your pages in several ways.
Real-World Example: How to Display a Custom List of Items With a Shortcode
I will show you how to build a quick and easy shortcode you can include in your content. Anyway, I won’t dive deep into shortcodes, as we already covered that topic in a previous blog post.
First off, create a new directory in the wp-content/plugins
folder of your local WordPress install or in a staging environment. In this example, I named the directory kinsta-shortcodes.
In wp-content/plugins/kinsta-shortcodes/
create a .php file with the same name as the new directory: kinsta-shortcodes.php
.
Open the new file in your favorite text editor and include the following heading:
<?php
/**
* @package Kinsta_shortcodes
* @version 1.0
*/
/*
Plugin Name: Kinsta shortcodes
Plugin URI: http://wordpress.org/extend/plugins/#
Description: This is an example plugin
Author: Your Name
Version: 1.0
Author URI: https://yourwebsite.com/
*/
Now we have a brand new plugin, but it’s still doing nothing. Browse to Plugins admin screen in your WordPress dashboard and activate the new plugin making sure you’ve WP_DEBUG
set to true
in your wp-config.php
file.
Your sandbox is now ready for your hacks. The next step is to register a hook for a custom shortcode:
/**
* Add a hook for a shortcode tag
*/
function kinsta_shortcodes_init(){
add_shortcode( 'kinsta_get_posts', 'kinsta_get_posts_cb' );
}
add_action('init', 'kinsta_shortcodes_init');
kinsta_get_posts
is the shortcode name and kinsta_get_posts_cb
is the callback defined below:
/**
* Register a shortcode
*
* @param array $atts Array of shortcode attributes
*/
function kinsta_get_posts_cb( $atts ){
// safely extract custom arguments and set default values
extract( shortcode_atts(
array(
'numberposts' => 3,
'post_type' => 'post',
'book_category' => 'fantasy',
'year_published' => 1900,
'price_min' => 0,
'price_max' => 50
),
$atts,
'kinsta_get_posts'
) );
// define the array of query arguments
$args = array(
'numberposts' => $numberposts,
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => 'book_category',
'field' => 'slug',
'terms' => $book_category,
)
),
'meta_query' => array(
'relation' => 'AND',
'year_clause' => array(
'key' => 'year_published',
'value' => $year_published,
'type' => 'numeric',
'compare' => '>',
),
'price_clause' => array(
'key' => 'price',
'value' => array( $price_min, $price_max ),
'type' => 'numeric',
'compare' => 'BETWEEN',
)
),
'orderby' => array( 'price_clause' => 'ASC' )
);
$custom_posts = get_posts( $args );
if( ! empty( $custom_posts ) ){
$output="<ul>";
foreach ( $custom_posts as $p ){
$output .= '<li><a href="'
. get_permalink( $p->ID ) . '">'
. $p->post_title . '</a> ('
. get_post_meta( $p->ID, 'year_published', true )
. ') - Price: ' . get_post_meta( $p->ID, 'price', true ) . '</li>';
}
$output .= '</ul>';
}
return $output ?? '<strong>Sorry. No posts matching your criteria!</strong>';
We set six shortcode attributes we use to define an array of parameters, which is finally passed to the WordPressget_posts
function. If $custom_posts
is not empty, then a foreach
cycle generates the HTML of an unordered list of items.
Now you and the authors of your blog can include lists of posts using a shortcode like the following:
[kinsta_get_posts post_type="book" book_category="sci-fi" numberposts="4" price_min=1 price_max=250]
Of course, you can change the array of arguments as you like and run your tests in any post or page of your development website.
Summary
WordPress get_posts is a powerful function that allows developers to include lists of posts anywhere on the frontend of your WordPress website. It uses WP_Query
but it’s easier to use and is preferable to WP_Query
when you just need lists of posts. Anyway, a direct reference to WP_Query
is recommended when you need to display posts in a Loop.
So, build your lists, test your code, and when you’re sure it works fine, then (and only then) push it to your live website (but run a backup first).
Now we would like to hear from you. What is your experience with the WordPress get_posts
function? Do you have any use cases to share with us? Do so in the comments below!