How to Add Post Filters to Your WordPress Site

A frequent request I come across is the ability to let users filter, or sort, posts on the front-end of their website.

Perhaps users want to view posts alphabetically, or maybe see only those posts with thumbnails? This already makes sense for regular posts but can be even more meaningful in the case of products, photos or other content types.

In today’s Weekend WordPress Project I’ll give you a quick rundown of how you can implement a feature like this in the Twenty Fifteen theme. Let’s get cracking!

Creating A Child Theme

As always, you need a child theme. We have a guide to child themes right here on WPMU DEV, I recommend giving that a read if you are not familiar with child themes.

Creating Controls

Let’s add three controls: one for ordering the posts, one for setting the direction of sorting and one for showing only posts with thumbnails.

The first step is to copy the parent theme’s index.php into our child theme.

Open the index.php file in your child theme and paste the following HTML below the main container (which should be on line 20):

And here’s what it looks like on the front-end:

Forms
Not so pretty yet but our form is in the right place

As you can see we are lacking a bit of styling. Let’s resolve that by adding some styles to the stylesheet:

Styled Filter
More work could be done but it looks much nicer

A reaction I frequently hear from newcomers to programming is: “how did he know that these are the styles that will make it blend into the theme?”

The solution is pretty simple: I cheat. I use the developer tools in Chrome to inspect the regular article elements. In this case, it let me see how the elements get their box shadow and their margins and I simply applied these rules to my own element.

Modifying the Query

Let’s select “order by title,” “ascending” and “posts with thumbnails” and submit the form. You should actually see a change without doing anything to the code.

To see why, let’s inspect the URL. It should be something like this:

http://yourdomain.com/?orderby=post_title&order=DESC&thumbnail=only_thumbnailed

The tidbits of information can be recalled in our PHP scripts using the $_GET variable. WordPress already knows what the order and orderby parameters mean and it uses them in the default query. As a result, if we only need ordering and order direction we’re actually all done.

That’s all great, but again, how did I know this? I could have used “order_by” as a parameter instead of “orderby.” In this case WordPress does not pick up on our intentions. I had a look at the WP_Query documentation in the WordPress Codex where there are a bunch of parameters, many of which can be used in URLs.

Now, let’s implement our post thumbnail parameter. A post has a thumbnail if it has metadata with the key _thumbnail_id associated with it. We’ll need to modify our query to make sure this is taken into account. Let’s do this now with query_posts().

Paste the following code above the get_header() function at the top of the file:

We merge the parameters of the original query with our own new parameter, which results in a different set of posts. Our form now works but it doesn’t remember our selections. Let’s fix that by rewriting our form and using some PHP.

Smarter Forms

In addition to listing all the options of the order by selector we need a way to indicate which one is selected. If we were to do this without a loop it would look something like this:

Do you understand any of that? I don’t blame you! Within each options we’re checking if the currently selected value is equal to the value of the option. If it is, we output the selected property. Let’s make this a lot cleaner with a loop:

This is a bit longer but only because we have three options. This is a way better format for managing any kind of selection. Let’s extend this to the whole form:

All done. The form should now remember our selections based on the $_GET variables in the URL.

WordPress Behavior

Remember how I mentioned that I know to use “order” and “orderby” because I looked at the WP_Query documentation? This is good practice, but it may lead to unexpected results. Find the slug of a category you have, say this category is “wordpress.”

Now use the following URL: http://yourwebsite.com/?category_name=wordpress. You should see your category archive, listing all your WordPress posts. This is just fine, but we have two problems:

If you have pretty permalinks turned on (which you should), the page has been redirected to a new URL, most probably http://yourwebsite.com/category/wordpress. Our filters will not be visible because the archive.php file handles this view, not index.php. In addition, our category name is not passed as a URL parameter so we will need to use some additional trickery to make our filters work.

The shortcut way of making this work is to deliberately not use the same parameters WordPress uses. You could pass the category name using the catname parameter in the URL since WordPress will not pick up on this. You can then feed this to the query using the correct parameter name. Something like this:

The alternative would be to use a function instead of outputting our form in index.php as is. You would need to detect the category from the WordPress query itself and display the current selection based on that.

Conclusion

Adding your own filters is not all that difficult but requires a bit of fiddling. In our case you may want to make sure the pagination is removed when the order is set to random. It could be replaced by a “show more randomness” button, which simply reloads the page.

Hopefully this article has given you the basics of how you can accomplish this for yourself and you will be able to build the filters you need.

If you have any questions about this project, feel free to ask in the comments.

19 Responses

  • New Recruit

    Hello, Daniel.
    I’m trying to extend this as a multi-filter (by custom_field and title), as wp 4.1 allows that (multiples orderby, by array). The meta_key part is working like a charm, but I’m a little bit lost about how did you pass your orderby and order arguments to the modified query. Could you clarify this?
    Thanks in advance,
    Roger.

  • New Recruit

    Hi @danielpataki,

    Thanks for the response, that’s similar to my main query, which is below, I think my main trouble is getting the inputs from the form to the query if that makes sense.

    So the below code, is the base of what should be displayed before any filters are applied, then if someone just wants to view Male users the main query below is used plus the meta_key gender with the value Male would be applied if that makes sense, I can’t quite get my head around that bit.

    $args = array(
    ‘role’ => ‘talent’,
    ‘order’ => ‘DESC’,
    ‘orderby’ => ‘user_registered’,
    ‘meta_key’ => ‘position’,
    ‘number’ => ’15’,
    ‘meta_query’=> array (
    array(
    ‘key’ => ‘picture’,
    ‘value’ => array(”),
    ‘compare’ => ‘NOT IN’
    ),
    array(
    ‘key’ => ‘position’,
    ‘value’ => $title
    )
    )
    );
    $wp_user_query = new WP_User_Query($args);
    $authors = $wp_user_query->get_results();
    if (!empty($authors))
    {
    foreach ($authors as $author)

    • Ah, I see!

      We’ll, you can do this via AJAX or not, just like in the article. If you do NOT use AJAX you can simply pass it as a parameter in the URL. So you could have your query working above just fine at http://site.com/show-all/. You could have a link which takes the user to http://site.com/show-all/?gender=male. The code would look like this:

      $args = array( /* Your usual args here, just like above */ );
      if( !empty( $_GET[‘gender’] ) ) {
      $args[‘meta_query’][]= array(
      ‘key’ => ‘gender’,
      ‘value’ => $_GET[‘gender’],
      ‘compare’ => ‘=’
      );
      }

      $wp_user_query = new WP_User_Query($args);

      Something like that. If you want to use AJAX it’s a little more complex but hopefully following the article once you’ve done it without AJAX should be easier :)

      Daniel

  • New Recruit

    Hi,
    First, thank you. You’re instructions actually work and were easy to follow! I’m wondering about adding some functionality to the existing framework. I have plugin called WP-PostViews that I would like to use to filter the results not just alphabetically or by newest/oldest but also by most popular. The problem I’m coming across is the inability to add a meta_key in to the orderby_options. The meta key is:
    $meta_key = ‘views’; or $meta_key = ‘the_views’;
    I’m confused as to which it is but it’s only 2 possiblities so I can hack away until the answer is right. So the best I’ve been able to come up with after a few hours of trying to figure out how to do this is the following non-working and silly looking code. What would I have to do to pass a meta_key value in to the orderby call? Or would I have to take an entirely different route?

    ‘Order By Date’,
    ‘post_title’ => ‘Order By Title’,
    ‘meta_value_num’ => ‘Most Popular’,
    ‘rand’ => ‘Random Order’,
    );
    foreach( $orderby_options as $value => $label ) {
    echo “$label”;
    }
    ?>

    Please keep in mind I am very new and won’t understand a theoretical answer and would appreciate a working copy/paste type example. Thank you for your time.

  • Hi Holly,

    I think you are misunderstanding the syntax a bit. I’ll just paste something I think that will work, I think it will be easy to understand :) Here’s an example of a completely ordinary query, but ordered by a custom field.

    $args = array(
    ‘post_type’ => ‘post’,
    ‘post_status’ => ‘publish’
    ‘meta_key’ => ‘age’,
    ‘orderby’ => ‘meta_value_num’,
    ‘order’ => ‘ASC’,
    );
    $query = new WP_Query( $args );

    The idea is that the orderby parameter tells wordpress how to order posts. This could be “title” or “date”, or a few other things. If you need to order by a meta field it should be “meta_value” or “meta_value_num”. Use the former if your values are simple text, use the latter if they are numbers. Then, also specify the “meta_key”, this is the meta key based on which the ordering is done.

    I think you may be underestimating the difficulty of this problem, because there is no built in way WordPress tracks popularity, this is something you must use a plugin for, or you have to build for yourself. In other words, this will only work for you if you already have a meta field which contains the relative popularity of an article, or some other metric like views which would allow you to order by.

    Daniel

  • New Recruit

    Daniel,
    Thank you very much for your fast response. I’ll be playing with this code over the weekend. I do have a plugin wp-postviews that counts, well, post views to use for the meta field. Just in case you see something screwy in this before I devote a lot of time to working it out I have:

    $orderby_options = array(
    ‘post_date’ => ‘Order By Date’,
    ‘post_title’ => ‘Order By Title’,
    ‘meta_value_num’ => ‘Most Popular’,
    ‘rand’ => ‘Random Order’,

    and I’m going to stack this on top of it:
    $args = array(
    ‘meta_key’ => ‘views’,

    for something that looks like this:

    $args = array(
    ‘meta_key’ => ‘views’,
    $orderby_options = array(
    ‘post_date’ => ‘Order By Date’,
    ‘post_title’ => ‘Order By Title’,
    ‘meta_value_num’ => ‘Most Popular’,
    ‘rand’ => ‘Random Order’,

    • Hi Holly,

      You’re still a bit confused about the syntax, plain English won’t work. based on what you wrote I’m not quite sure what you’d like to do, order by date or title, but if you want to order by views this is what you’ll need:

      $args = array(
      ‘meta_key’ => ‘views’,
      ‘orderby’ => ‘meta_key_num’
      )

      and that’s basically it.

      Daniel

  • New Recruit

    I’ll try to explain as best I can. I’m trying to do what is in this post. I want to have a front end where visitors can choose to sort by title, date and views. The tricky part seems to be the views since that requires a meta key. I want to retain the ability to sort by date and title but just add the ability to sort by views as well. Sorting by views would be one more of the drop down options listed in among title and date that the visitor would see. They could then choose any one of those options.

  • New Recruit

    Hi! I realize this is an older post, but I’m trying desperately to modify this to fit my purposes, and I’m having a heck of a time.

    All I want to do is to add the functionality to sort posts by the numeric value of a custom field called “likes”. I feel like it should be easy, but I cannot for the life of me figure it out. :P I saw the previous comment thread that was sort of similar, but I couldn’t figure out how to apply what I read there. Any suggestions?

    Thanks, in advance! (Fingers crossed that you see this comment.) :)

Comments are closed.