I have a custom post type that has custom meta that I want to be able to filter on the Admin page. Much like the "All | Published | Drafts | and Trash" links located above the post list. I can't seem to find a hook to hook into. Does one exist?

This question isn't exactly what I am asking. I would prefer not to have the filter dropdown, but instead a link like …

All | Published | Drafts | Trash

… across the top of the editor.

  • 3
    check the first question in the related column over there ->
    – Milo
    Commented Apr 4, 2013 at 21:48
  • 1
    I have seen this, but what I was asking is can you add a link to the list similar to the "All|Published|Drafts|Trash" links. My client finds the drop down a bit confusing, and would prefer a simple click a link setup.
    – micahmills
    Commented Apr 5, 2013 at 3:28

2 Answers 2


Just like in Adding a Taxonomy Filter to Admin List for a Custom Post Type? the filter parse_query could be used, but here I'm using posts_where.

The row All | Published | ... is controlled by views_edit-{$post_type} and the $views array contains each item that's a simple anchor tag.

First, we insert a couple of links - a separator and a Meta filter:

add_filter( 'views_edit-portfolio', 'meta_views_wpse_94630', 10, 1 );

function meta_views_wpse_94630( $views ) 
    $views['separator'] = '           ';
    $views['metakey'] = '<a href="edit.php?meta_data=allorany&post_type=portfolio">Meta Key</a>';
    return $views;

The link contains meta_data=allorany (the custom field name), which will be used to filter by meta key.

enter image description here

And then, filter when needed:

add_action( 'load-edit.php', 'load_custom_filter_wpse_94630' );

function load_custom_filter_wpse_94630()
    global $typenow;

    // Adjust the Post Type
    if( 'portfolio' != $typenow )

    add_filter( 'posts_where' , 'posts_where_wpse_94630' );

function posts_where_wpse_94630( $where ) 
    global $wpdb;       
    if ( isset( $_GET[ 'meta_data' ] ) && !empty( $_GET[ 'meta_data' ] ) ) 
        $meta = esc_sql( $_GET['meta_data'] );
        $where .= " AND ID IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key='$meta' )";
    return $where;
  • 1
    +1, this is slick. Though I do feel that somebody having "clients" ought to be able to modify Mike Schinkel's answer in the near-duplicate to their needs... Commented Apr 5, 2013 at 14:28
  • 1
    Thanks for this answer it was incredibly helpful. For whatever reason I missed the views_edit-{$post_type} and $views thing. @Johannes Thank you for your piety, I will be sure to point all my clients in your direction in the future.
    – micahmills
    Commented Apr 5, 2013 at 15:59
  • While probably still true, that might have been a tid bit of a snooty remark @micahmills, I'll give you that. Commented Apr 5, 2013 at 16:06
  • 1
    Had all kinds of problems with this that were hard to diagnose. I think because the second filter never gets removed, and so gets applied to all further queries, for any post type. See additional answer add with alternate solution
    – mozboz
    Commented Sep 13, 2022 at 19:14
  • 1
    @mozboz, thanks for pointing that out. I did a search for posts_where and remove_filter here on WPSE and it yelds many results, certainly I'm missing removing the filter somewhere.
    – brasofilo
    Commented Sep 13, 2022 at 19:57

With credit to @brasofilo for his answer, it gave me all kinds of problems for a reason I couldn't quite diagnose. I think the posts_where filter that gets added needs to be removed otherwise it will run for all future queries in the page and make a mess of anything that then tries to query a different post_type as the second filter will still be active.

Here's a solution that doesn't require hacking the SQL manually, but works on the Query object instead. Assuming using the method of adding URL params as in the other answer here with url param name cpt_filter

function filter_cpt($query) {

    global $pagenow;

    $cpt = "your_custom_type_name";
    $cpt_key = "some_key_to_filter_on";
    $cpt_value = "some_value_to_filter_for";

    if (is_admin() && $pagenow=='edit.php' &&
        isset($_GET['post_type']) && $_GET['post_type']==$cpt &&
        isset($_GET['cpt_filter'])  && $_GET['cpt_filter'] != 'None' &&
        $query->query['post_type'] == $cpt)  {
          $query->query_vars['meta_key'] = $cpt_key;
          $query->query_vars['meta_value'] = $cpt_value;


add_filter( 'parse_query', 'filter_cpt' );

No problems here with adding/removing filters, and flexibility with setting the meta key and value to whatever works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.