Order WordPress Pages By Multiple Fields

Using the standard query_posts() method with WordPress, it’s extremely simple to dictate in what order you want your posts and/or pages displayed when they are being shown together; as long as you only want to sort them by a single criterion. However, if you want to sort them by more than one criterion, then you have to do a little more work.

Sorting by One Field

To begin with, I will show you an example of sorting your posts or pages by a single criterion.

query_posts('orderby=post_title');
if(have_posts()) : while(have_posts()) : the_post()
// Perform your standard "the loop" actions
endwhile; endif;

You can sort your posts and/or pages by any of the following criteria: author, date (posted date), post_title, modified (modified date), menu_order, parent, ID, rand (random), meta_value (a meta_key=keyname must also be declared in the query), none (no order) and comment_count.

Sorting by Multiple Fields

I, however, had a single page I wanted to show up first in the list, then I wanted all of the other pages to show up in alphabetical order. To accomplish this, first I assigned a menu_order of 0 to the page I want listed first and 1 to all of the other pages.

To make this happen, I tried a few different variations of the standard query_posts() method, but couldn’t get it to work for me. I tried feeding an array as the value of the orderby clause, and then I tried feeding it a simple comma-separated list. The array generated an error message telling me that the function wouldn’t accept an array for that parameter. The comma-separated list seemed to ignore the second parameter altogether.

I then started looking for ways to run custom queries with WordPress. I came across an article in the WordPress codex explaining how to use custom SELECT queries. I then started to work on my own custom query.

To be safe, I stored the current current post ID variable and the $post and $wp_query objects in new temporary variables, as I needed to be able to access them again after I was done using my custom query. That looked something like:

global $post;
$current_page_id = $post->ID;
$original_post = $post;
$tmpquery = $wp_query;

Then, I started building my query. That ended up looking like:

$querystr = "
	SELECT wposts.*
	FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
	WHERE wposts.post_parent = $current_page_id
	AND wposts.ID = wpostmeta.post_id
	AND wposts.post_status = 'publish'
	AND wposts.post_type = 'page'
	ORDER BY wposts.menu_order, wposts.post_title ASC
 ";

When I ran this query and started my custom loop (explained in the article I linked above), I noticed that I was getting two copies of each post. I first solved this issue by creating an empty array (in my case, I called it $used) and adding each post to that array when I looped through. If I found that the current post already existed in the $used array, I skipped it by using code similar to:

if(in_array($post,$used)) {
    continue;
}
array_push($used,$post);

Since I was not actually using any of the post meta data, I didn’t think I really needed to query the second table or perform the JOIN (“wposts.ID = wpostmeta.post_id“), but I went ahead and left that in there just in case I need it at a later date.

I then decided to test the query with that information removed. Lo and behold, I figured out that, without querying both tables and performing the JOIN, I no longer ended up with doubles of each post, so I was able to remove the $used array. My code now looks like:

$querystr = "
	SELECT wposts.*
	FROM $wpdb->posts wposts
	WHERE wposts.post_parent = $current_page_id
	AND wposts.post_status = 'publish'
	AND wposts.post_type = 'page'
	ORDER BY wposts.menu_order, wposts.post_title ASC
";

I also removed the $used array from my code since I no longer needed it.

So, although I couldn’t use the standard query_posts() method, I was able to figure out how to successfully sort my pages by menu_order then post_title by simply using the get_results() method of the WordPress database object.