in WordPress
Programmatically filter post archives in WordPress using URL params and WP_Query
Find out why filtering pages based on specific criteria improves the UX of your website and how to use URL params and the WP_Query to create a WordPress friendly approach to filtering.
WordPress is one of the most popular CMS on the market and it is widely used to create content driven blogs and websites. A great feature of WordPress is the ability to create archive pages to list your posts and/or any custom post type of your choice. A list could look like the following:
However, what if you want to filter those posts based on specific criteria, such as a custom field, a custom taxonomy, or a combination of different parameters? In this blog post, we will explore how to use URL parameters and the WP_Query to create a WordPress / browser friendly approach to filtering.
Why do URL parameters matter
URL parameters, also known as query parameters, are a crucial aspect of web development when it comes to filtering and synchronizing data between the server and client. They can be used to:
- store state that persist in both the browser and the server
- create shareable URL with predefined options, filters, sorting etc.
- save filters, sorting and/or search between navigation
Working with WP_Query
Before we dive into filtering using URL parameters, let’s see how the WP_Query class works. WP_Query is a class in WordPress that allows us to query posts based on different parameters such as post type, category, author, etc. We can create a new instance of WP_Query with the parameters we want to filter on, and it will return an array of posts that match those parameters. By default WordPress allows us to use some of the WP_Query parameters for filtering via the URL.
Pass filter parameters to the URL
We can leverage the browser native behavior to pass URL params using forms. For our first example we will demonstrate how to filter an archive page based on a category ID. The markup for the form would look like this:
<form>
<label for="cat">Category</label>
<select name="cat" id="cat">
<option value="1">Category 1</option>
<option value="2">Category 2</option>
</select>
<input type="submit">
</form>
When we submit this form the browser will reload and the URL will look something like this:
https://domain.com/archive-page/?cat=category-id
Where category-id is the values of the option we selected. WordPress will automatically then filter our archive page to only show posts from the selected category.
We can also filter based on the array of items like tags for example. In the next example we will also add a multi-select to filter by specific tags.
<form>
<label for="cat">Category</label>
<select name="cat" id="cat">
<option value="1">Category 1</option>
<option value="2">Category 2</option>
</select>
<label for="tag">Tags</label>
<select name="tag[]" id="tag" multiple>
<option value="1">Tag 1</option>
<option value="2">Tag 2</option>
</select>
<input type="submit">
</form>
If we select the category with the id of 1 and both of the tag our URL will look something like this:
https://domain.com/archive-page/?cat=1&tag[]=1&tag[]=2
WordPress will automatically then filter our archive page to only show post from the selected category and tags.
Adding custom parameters
There is also a way to add your own custom parameters for modifying the query results. Let’s see how we can do this. For this example we will add a custom value for sorting our archive pages based on a parameter that look like this sort=date-ASC or sort=date-DESC
WordPress by default won’t understand those parameters but we can hook into the pre_get_posts action to change that.
add_action('pre_get_posts', function ($query) {
if ($query->is_main_query() && !is_admin()) {
if ($query->get('post_type') == 'post') {
if (!empty($_GET['sort'])) {
$orderParam = explode('-', $_GET['sort']);
$orderBy = $orderParam[0];
$order = $orderParam[1];
$query->set('orderby', $orderBy);
$query->set('order', $order);
}
}
}
});
On this code we first check if we are on the main query and not on the admin area. Optional we check if the post type is post – if we want to accept this params in all out post type the we can omit this check. Then we check to see if the sort parameter is included in the url. If then the param is present we split the value to extract our orderBy and order values for the query. Finally we simply modify the query accordingly and we have a custom param ready to be used on our frontend.
We can extend this functionality as much as we want to create a truly tailor-made experience for our website.
As a rule of thumb, we would recommend to try and achieve as much as possible using the default functionality and only add custom parameters if you really need to.
Show active filter state in UI
Finally, as a last step, we can parse the URL params in the server and set the selected filters in the UI when we submit the form and the page reloads. The example below demonstrates how we could achieve this in Flynt but we can use a similar approach for vanilla WordPress for example.
add_filter('Flynt/addComponentData?name=GridPostsArchive', function ($data) {
$postType = 'post';
$taxonomy = 'category';
$terms = get_terms([
'taxonomy' => $taxonomy,
'hide_empty' => true,
]);
// Get the params from the URL on the server
$queryVar = $_GET['cat'] ?? '';
// Create an array with all selected terms
$selected = empty($queryVar) ? [] : (is_array($queryVar) ? $queryVar : explode(',', $queryVar));
if (count($terms) > 1) {
// Set the term as selected if it is included on the query vars
$data['terms'] = array_map(function ($term) use ($selected) {
$timberTerm = Timber::get_term($term);
$timberTerm->selected = in_array($term->term_id, $selected);
return $timberTerm;
}, $terms);
}
return $data;
});
We can then use the selected property from the terms array when we create the filters to assign the corresponding attribute to the form with our filters. This way we now have our server, client and UI synchronized. For example:
<form>
<label for="cat">Category</label>
<select name="cat" id="cat">
{% for term in terms %}
{% if term.id %}
<option
name="cat"
value="{{ term.id }}"
id="{{ term.id }}"
{{ term.selected ? "selected" }}
>
{{ term.name }}
</option>
{% endif %}
{% endfor %}
</select>
<input class="button" type="submit">
</form>
Conclusion
Filtering WordPress archive pages using URL parameters in combination with WP_Query is a powerful technique that can help you create more dynamic and user-friendly websites. By allowing users to filter content based on their preferences, you can improve the user experience and make your website more engaging. Use this technique wisely and test it thoroughly, and you will be able to create amazing archive pages that meet your clients‘ needs.