Skip to content

Modifying WordPress Query to Order By Custom Field

Sun 10th November 2013

One of my current projects is to generate a a printable newsletter from some selected blog posts in my WordPress-driven site. I expected there to be something that would take an RSS feed and generate a PDF, but no such luck. Pat pointed out that I could use WordPress to tag the things that needed to go into the newsletter, and a custom field to order them. This afternoon I tried to implement the ordering.

I think WordPress is great. It is open source, it’s relatively easy to set up, and it’s extremely powerful and flexible. However, it became apparent that while it has lots of documentation, it’s not great for finding the answers to questions. It is a pretty good reference and has lots of “what”, but there is a lack of “why” and “how”.

For example, I realised that to change the order of the posts that are displayed, I would need to modify the query that WordPress runs. A lot of resources on the web suggest solving it using the query_posts() function, but if you read the reference page it says you shouldn’t use it if you can avoid it. It’s pretty verbose and vague about why. But that’s fine, I moved on to it’s suggested method: the pre_get_posts action.

I then spent a whole afternoon trying variations of query values and positions to put the code in my template. Tried it ordering by my custom field, ordering by name, ascending and descending. I tried putting the code at the top of the template, after wp_head() is called, directly before “The Loop”. Nothing worked.

Eventually I discovered, thanks to the second SlideShare on this topic that I looked at (slide 38), that the call to add_filter('pre_get_posts', ...) (or presumably add_action() – it would be another afternoon’s reading to figure out the difference between those) has to be in functions.php because that happens before the query is initially run. It would have been pretty useful to point that out in the page about add_filter!

Anyway, so this might be useful to someone, here is the code I added to functions.php:

<?php
	function order_newsletter( $query )
	{
		if( is_tag() && preg_match("/.*road.*runner.*/i", get_query_var('tag')) )	// if tag page and has road runner in tag name
		{
			$query->query_vars['meta_key'] = 'newsletter_order';
			$query->query_vars['orderby'] = 'meta_value_num';
			$query->query_vars['order'] = 'ASC';
		}
	}
	add_filter( 'pre_get_posts', 'order_newsletter' );
?>

Things to point out:

  • $query is a live reference to the actual query object being used, modifications to it actually happen: no need to return it
  • meta_key is the custom field you wish to order by
  • meta_value_num tells the query to treat meta_value as a number (you can also use date)
  • once you’ve added the filter for pre_get_posts, it will happen for every query, which is why it’s useful to have an if statement to only apply it to the appropriate pages
Advertisements

From → Programming, Rant, The Web

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: