I have a lot of custom columns going on over at LezWatch TV. And there are a million tutorials on how to make your columns sortable. So here’s another one.
Backstory
I have a custom post type for TV characters, and the characters have extra meta data. The kind I want to show is the TV show (or shows) associated with the character and the role type.
I should note that ‘role type’ can be weird, since there are characters who will be guests on one show and regulars on the other, and really I should go back and change all of that but when you get to 1000 characters, you really don’t want to… Anyway. Let’s just do this. Keep in mind the following things.
- The custom post type was registered as
post_type_characters
- The data I wish to list is ‘tv shows’ and ‘role type’
- TV Shows is an array of IDs related to another post type (shows), stored in a meta key called
lezchars_show
- Role Type is a plain text value stored in
lezchars_type
Now we’re ready to go
New Column Headers
We want to make a new column for your custom post types. For this to work, you need to know the name of your custom post type – post_type_characters
remember – and the format for the column code – manage_{POSTTYPE}_posts_columns
… See how that works?
// Add Custom Column Headers add_filter( 'manage_post_type_characters_posts_columns', 'set_custom_edit_post_type_characters_columns' ); function set_custom_edit_post_type_characters_columns($columns) { $columns['cpt-shows'] = 'TV Show(s)'; $columns['postmeta-roletype'] = 'Role Type'; return $columns; }
The name of the columns (cpt-shows
and postmeta-roletype
) are semi-arbitrary. I picked cpt-TYPE
and postmeta-META
because they were logical to me. I always use those formats.
Column Content
Giving the column content is a little more difficult because I’m listing TV shows associated with a character. Plural. And some characters are on three shows. Usually people are only on one, thankfully.
This time the function name is going to be based on manage_{POSTTYPE}_posts_custom_column
// Add Custom Column Content add_action( 'manage_post_type_characters_posts_custom_column' , 'custom_post_type_characters_column', 10, 2 ); function custom_post_type_characters_column( $column, $post_id ) { // Since SOME characters have multiple shows, we force this to be an array if ( !is_array( get_post_meta( $post_id, 'lezchars_show', true ) ) ) { $character_show_IDs = array( get_post_meta( $post_id, 'lezchars_show', true ) ); } else { $character_show_IDs = get_post_meta( $post_id, 'lezchars_show', true ); } // Show Title is an array to handle commas $show_title = array(); foreach ( $character_show_IDs as $character_show_ID ) { array_push( $show_title, get_post( $character_show_ID )->post_title ); } switch ( $column ) { case 'cpt-shows': echo implode(", ", $show_title ); break; case 'postmeta-roletype': echo ucfirst(get_post_meta( $post_id, 'lezchars_type', true )); break; } }
And this results in this:
Making it Sortable
But as you’ve noticed, it’s not actually sortable. That is, if I have one character with no ‘role’, I have to surf through 47 pages until I find her. So the magic here is to make these columns sortable.
This time the filter is named manage_edit-{POSTTYPE}_sortable_columns
and there’s some extra mess going on.
// Make columns sortable add_filter( 'manage_edit-post_type_characters_sortable_columns', 'sortable_post_type_characters_column' ); function sortable_post_type_characters_column( $columns ) { unset( $columns['cpt-shows'] ); // Don't allow sort by shows $columns['postmeta-roletype'] = 'role'; // Allow sort by role return $columns; }
The column names are required from the previous steps, but the sort by being ‘role’ is something I made up. It doesn’t have to match anything from before however it has to be remembered, as we’re going to re-use it in a moment.
You see, I have to tell it what ‘sortable’ means by filtering pre_get_posts
. I’m grabbing the meta value for lezchars_type
and I’m going to tell it to order by the value. This happens to be alphabetical. If I was using a number, it would have to be meta_value_num
instead of meta_value
.
// Create Role Sortability function post_type_characters_role_orderby( $query ) { if( ! is_admin() ) return; if ( $query->is_main_query() && ( $orderby = $query->get( 'orderby' ) ) ) { switch( $orderby ) { case 'role': $query->set( 'meta_key', 'lezchars_type' ); $query->set( 'orderby', 'meta_value' ); break; } } }
Remember how I said to remember we called the order-by ‘role’? Well this was why. The case check is on ‘role’ but also the meta_key is our key name.
And yes it works: