How to Make WP_Query Arguments Filterable
WP_Query class is very powerful. It lets you create your own custom queries to run anywhere in your WordPress site – in the main content, in the sidebar or anywhere else you like.
It’s something I use a lot, either in custom template files or in areas outside the content such as the sidebar or footer. And I’ve lost count of how many times I’ve coded a custom query using
But it is possible to avoid all that rework. If you make your query arguments filterable, you can write a function to amend the arguments and run a different query in different places in your site. This means you can write a plugin with some default arguments (or indeed with no arguments at all), and then write a function in your theme (or in another plugin) that amends those arguments.
This won’t change the loop that runs using your query arguments (although if you want you could also create another filter for that), but it does mean you can code your
WP_Query instance once and then tweak it when you need to.
In this post, I’ll show you how to write a plugin with a filterable instance of
WP_Query and then write a function in your theme’s functions file to edit the arguments.
What You’ll Need
To follow along with this post, you’ll need access to a couple of things:
- A development or testing installation of WordPress running your own theme or a child theme
- A code editor
Ready? Then let’s begin!
Writing the WP_Query Plugin
Start by creating your plugin. Create a new folder for your plugin in your wp-content/plugins folder, and then create a blank file inside that. I always create a folder in case I want to add any styles, scripts or include files to my plugin at a later date.
Here’s the opening lines of my plugin:
Now let’s add the
WP_Query function. I’ll start by adding the
$args variable, but keep it empty:
Then we add the loop:
Here’s the full function:
We now have a fairly standard query and loop. This will run a query based on the arguments (which are currently empty), then output a heading followed by a list of the items fetched with links to them. It could be used to output a list of posts using categories, taxonomies, post types or anything else as the arguments.
Right now though, it won’t output anything, as those arguments are empty. Let’s add some arguments but wrap them in a filter.
Adding Filterable Arguments
$args section of your code and edit it to add some arguments.
I’m just going to add an argument for
posts_per_page, to limit the number of posts output. I won’t add any other arguments: this way the most recent five posts will be output. If you’d like, you can add some different arguments.
So far, so straightforward. Now let’s enclose those arguments in a filter. Here’s the code:
This wraps our single argument in a filter called
wpmu_filterable_query, which you can then hook into from another plugin, or from your theme, to amend those arguments.
1.6 million WordPress Superheroes read and trust our blog. Join them and get daily posts delivered to your inbox - free!
While we’re at it, let’s add a filter to that header inside the loop, as it’s a bit generic.
Edit your loop so it includes a filter:
And that’s it. Unless you decide to add some more filters to the loop, your plugin is ready.
Using the Filter in Your Theme
The next step is to write a couple of functions in your theme.
The first will call the
wpmu_filterable_query action hook and output it in your page. You can call this in one of a number of ways.
The first is to attach it to an action hook in your theme, by using the
add_action function. So if your theme had a hook called
my_theme_sidebar_hook, you would output the query in the sidebar like this:
The second is to code it directly into a theme template file. I prefer to work with hooks where possible, as they give me more flexibility, but if your theme doesn’t have any hooks this might be the best approach. If you’re working with a third party theme, don’t edit the theme files directly – instead, create a copy of it in a child theme.
Then in your theme template file, add a call to the
This simply runs the function in the spot in your template file where you place it.
The third option is to create a new template file, such as a page template file, which will run this query instead of the default query. In this case you’d make a copy of page.php from your theme or your parent theme, and replace the standard loop with the call to the function, as above.
So that’s how you add the function to your theme. But how about filtering those arguments?
Filtering the Arguments in Your Theme
The final step is to write a function in your theme’s functions file to filter the query arguments. You can also add a second function to filter the heading, as well as any functions to make use of any other filters you might choose to add to the loop in your plugin.
Note that you could do this using a plugin if you wanted, but as you’ve already coded the function into your theme, I think it’s neater to add this code to your functions file.
Let’s imagine you’ve registered a post type called doohickey, and you want to output that instead. But instead of outputting six posts, you want to show four.
In your functions file, you’d need this code:
This replaces the contents of the original filter with the new contents in the function. Note that if you want to retain any of the arguments in the original filter, you’ll have to add them to this function, as the new code overrides the old code, and doesn’t add to it.
Next let’s add a function to edit the heading text:
That will output the content of the new function instead of
<h3>Heading</h3>, which was inside the filter.
You can amend both of these functions as you see fit.
Making WP_Query Filterable Makes Your Code More Efficient and Saves Time
If you’re going to be using the
WP_Query class in lots of sites and want to save yourself the bother of coding
WP_Query in full every time, this can save you some work. In each site where you use
WP_Query, you only need to add the call for the function, and the function to attach to the filter hook.
If you wanted to make your plugin even more flexible, you could use an include file for the loop instead of coding it directly into the plugin, and then enclose the
include_once() call in a filter. This way, you could call a different include file if you wanted to, and output a different version of the loop.