How to sort the admin area of a WordPress custom post type by a custom field in WordPress
As you can probably imagine by the lack of answers provided, the solution ist exactly trivial. What I've done is create a somewhat self-contained example that assumes a custom post type of "
movie
" and custom field key of "
Genre
".
Disclaimer
: this works with WP3.0 but I can be sure it will work with earlier versions.
You basically need to hook two (2) hooks to make it work and another two (2) to make it obvious and useful.
The first hook is '
restrict_manage_posts
' which lets you emit an HTML
in the area above the list of posts where the "
Bulk Actions
" and "
Show Dates
" filters. The code provided will generate the "
Sort by:
" functionality as seen in this screen snippet:
The code uses direct SQL because there ist a WordPress API function to provide the list of all meta_keys for a post types (sounds like a future
trac
ticket to me...) Anyway, here's the code. Note that it grabs the post type from
$_GET
and validates to make sure it is both a valid post type
post_type_exists()
as well as being a
movie
post type (those two checks are overkill but I did it to show you how if you don want to hard-code the post type.) Lastly I use the
sortby
URL parameter as it doesn conflict with anything else in WordPress:
add_action('restrict_manage_posts','restrict_manage_movie_sort_by_genre');
function restrict_manage_movie_sort_by_genre() { if (isset($_GET['post_type'])) { $post_type = $_GET['post_type']; if (post_type_exists($post_type) && $post_type=='movie') { global $wpdb; $sql=<<
postmeta} pm
INNER JOIN {$wpdb->
posts} p ON p.ID=pm.post_id
WHERE p.post_type='movie' AND pm.meta_key='Genre'
GROUP BY pm.meta_key
ORDER BY pm.meta_key
SQL; $results = $wpdb->
get_results($sql); $html = array(); $html[] = "
"; echo "Sort by: " . implode("\n",$html); } }
}
The second required step is to use the
parse_query
hook that is called after WordPress decides one what query is should run but before it runs the query. Here we get to set values of
orderby
and
meta_key
in the query's
query_var
array which are
documented in the Codex
in the
orderby
parameter for
query_posts()
. We test to make sure that:
We are in the admin (
is_admin()
),
We are on the page that lists posts in the admin (
$pagenow=='edit.php'
),
The page has been called with a
post_type
URL parameter equal to
movie
, and
The page has also been called with a
sortby
URL parameter and that it wasn passed a value of '
None
'
If all those tests pass we then set the
query_vars
(as documented
here
) to
meta_value
and our
sortby
value for '
Genre
':
And that's all you need to do; "
posts_order
" or "
wp
" hooks required! Of course you actually do need to do more; you need to add some columns on your page that lists the posts so you can actually see the values that it is sorting by otherwise the users will get mucho confused. So add a
manage_{$post_type}_posts_columns
hook, in this case
manage_movie_posts_columns
. This hook gets passed the default array of columns and for simplicity I just replaced it with two standard columns; a checkbox (
cb
) and a post name (
title
). (You can inspect
posts_columns
with a
print_r()
to see what else is available by default.)
I decided to add a "
Sorted By:
" for when there is a
sortby
URL parameter and when it ist
None
:
Finally we use the
manage_pages_custom_column
hook to actually display the value when there is a post of the appropriate post type and with the probably redundant test for
is_admin()
and
$pagenow=='edit.php'
. When there is a
sortby
URL parameter we extract the custom field value that is being sorted by an display it in our list. Here's what it looks like (remember, this is test data so comments from the peanut gallery on the movie classifications! :):
add_action('manage_pages_custom_column', 'manage_movie_pages_custom_column',10,2);
function manage_movie_pages_custom_column($column_name,$post_id) { global $pagenow; $post = get_post($post_id); if ($post->
post_type=='movie' && is_admin() && $pagenow=='edit.php') { switch ($column_name) { case 'meta_value': if (isset($_GET['sortby']) && $_GET['sortby'] !='None') { echo get_post_meta($post_id,$_GET['sortby'],true); } break; } }
}
Note that this only picks up the first "
Genre
" for a
movie
, i.e. the first meta_value in the case of multiple values for a given key. But then again I'mt sure how it would work otherwise!
And for those unfamiliar with where to put this code you can put it in a plugin or more likely for the newbie in the
functions.php
file in your current theme.