Filtering results on an archive or taxonomy page with pre_get_posts

We came across an interesting code challenge when building a site recently.

What we needed was to change the order of posts on our archive & taxonomy pages based on different post types.

By default archive & taxonomy pages output the results in post date order (when the post was created), usually newest first just like a blog.  This is perfect when your working with date based posts but not ideal if your building a business directory or an events calendar where you might need to filter by a custom field or by title etc.

What we had was a custom post type called events.

Our custom post type also had two unique custom fields, one for the start date and one for the end date.

Our main events page was a custom template with a custom query that showed events in date order (start date) and only displayed events that were either on going (end date not passed) or upcoming (start date in front of current date).

This is relatively easy as we use can grab todays date using the standard date format and compare it with the date added to the custom field.

One critical factor is to make sure the date used is the same format. For example our custom field date was set as Y-m-d which equated to 2015-08-24. This needs to match the current date grabbed from the $today variable otherwise it will not work.

 


$today = date('Y-m-d');
$args = array (
  'post_type' => 'event',
  'posts_per_page' => -1,
  'meta_key' => 'start_date',
  'orderby' => 'meta_value',
  'order'=> 'ASC',
  'meta_query' => array(
      array(
      'key'=> 'end_date',
      'compare'	=> '>=',
      'value'=> $today,
      )
  ),
);

 

 

Navigation wise we want users to drill down and find events based on event type.

This meant creating a custom taxonomy called event type and then using the standard WP menu to create a link to that particular category. We then had a custom taxonomy template that displayed the results.

The issue was the results simply came out in date order of when the events were created and all the events past and present where showing.

What we had to do was filter these results in the same way as our main events template, showing current & upcoming events events in start date order.

The solution uses a custom function which is added to your functions.php file.

Essentially it hooks into the query before the page is outputted and changes the results to suit our needs.

 


// change order of event_category pages to date based
function change_event_order( $query ) {

  // Make sure this only fires when we want it too
  if( !is_admin() && $query->is_main_query() && $query->is_tax('event_category')) {
    
    // If so, modify the order variables
    $query->set('meta_key', 'start_date' );
    $query->set('orderby', 'meta_value'  );
    $query->set( 'order', 'ASC' );
    $meta_query[] = array(
                array(
               'key' => 'end_date',
               'value' => date('Y-m-d'),
               'compare' => '>=',
                 ),
				 );
	$query->set('meta_query',array( $meta_query ) );
  }
  
}
add_action('pre_get_posts', 'change_event_order', 9999);

 

There are some critical sections to the above function, the first making sure that the filter fires on specific pages otherwise your essentially filtering every query front and backend by the new parameters.

 


if( !is_admin() && $query->is_main_query() && $query->is_tax('event_category')) {..}

 

Secondly make sure that you use [] with the $meta_query and make sure that you output this variable as an array to in $query->set(‘meta_query’,array( $meta_query ) );”

 


$meta_query[] = array(
                 array(
                 'key' => 'end_date',
                 'value' => date('Y-m-d'),
                 'compare' => '>=',
                 ),
		);
$query->set('meta_query',array( $meta_query ) );

 

The final and most important item is the hook order parameter ‘9999’. Without this the filter won’t fire correctly and the filter doesn’t work (could argue this is the most important realy!).

 


add_action('pre_get_posts', 'change_event_order', 9999);

 

Hope this helps, as we found hundreds of posts on this very subject, many flagging wordpress bugs. It was only through testing a large number of options that we finally got this to work using wordpress 4.3.1.

About the Author

Hi, I’m Mark Carpenter a Coffs Harbour based website consultant, designer and marketing strategist with a passion for helping you grow your business’s online. I have over 25 years industry experience & have worked all over the world.

Comments are closed, but feel free to share!!