Ordering WordPress Post Types By A Custom Field
Imagine you have a web site for a university course with a custom post type for Assignments, with each assignment having a due date stored in a custom field. You would probably then want to have the list of Assignments in the WordPress Dashboard area displayed in chronological order by this due date custom field. (I discuss how to set up the custom post type for this scenario in my talk at edUi 2011, “Advanced WordPress Features & Customizations.”)
To customize the query WordPress uses to display the list of posts in this manner, you add a filter to two hooks: posts_join and posts_orderby. When you add a filter to these hooks, WordPress will pass a string into your custom function:
posts_join
Your custom filter receives the part of the query that contains the list of tables to be joined together. You then need to add to this a left join to the relevant rows in the wp_postmeta table. (You can use a subquery to get only the due date rows.) In my example, the additional join looks like this:
add_filter('posts_join', 'rrh_assignment_join' ); function rrh_assignment_join($wp_join) { if(is_post_type_archive('rrh_assignment') || (is_admin() && $_GET['post_type'] == 'rrh_assignment')) { global $wpdb; $wp_join .= " LEFT JOIN ( SELECT post_id, meta_value as date_due FROM $wpdb->postmeta WHERE meta_key = 'date_due' ) AS DD ON $wpdb->posts.ID = DD.post_id "; } return ($wp_join); }
We do not want to apply this filter to every post listing, just the ones related to assignments. I have wrapped the code that adds to the string in a conditional that checks if it is the assignment archive page on the web site — is_post_type_archive(‘rrh_assignment’) — or if it is the assignment list page in the WordPress Dashboard area — is_admin() && $_GET[‘post_type’] == ‘rrh_assignment’. On every other page, the part of the query that lists out the tables to be joined together passes through unchanged.
posts_orderby
Your custom filter receives the part of the query that describes how the results should be ordered. You then need to replace this with your own ordering. In my example, the new order looks like this:
add_filter('posts_orderby', 'rrh_assignment_order' ); function rrh_assignment_order( $orderby ) { if(is_post_type_archive('rrh_assignment') || (is_admin() && $_GET['post_type'] == 'rrh_assignment')) { $orderby = " STR_TO_DATE(DD.date_due,'%m/%d/%Y') ASC "; } return $orderby; }
Just like before, we only want to apply this filter to the assignment archive page on the web site and on the assignment list page in the WordPress Dashboard.
Download
This article builds upon my talk at edUi 2011 on how to set up custom post types. You can view that presentation and download the necessary code to make all this work on the page for that presentation: “Advanced WordPress Features & Customizations.”