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.

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)) {

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.

Did you like this post? Get monthly summary of our new tutorials, posts and tips to your inbox!

No Responses

Post Your Comment

Your email address will not be published.