Add column and post filter for a custom post type field on the edit.php page

First couple of handy helper functions,

function my_custom_post_type() {
  return 'post'; // edit this
}

function custom_field_status_metakey() {
  return 'status';
}

function custom_field_status_options() {
  return array(
    ''       => __( 'All statuses', 'textdomain' ), // edit text domains
    'open'   => __( 'Open', 'textdomain' ),
    'closed' => __( 'Closed', 'textdomain' ),
  );
}

Add your custom column to the post list with manage_{post_type}_posts_columns and manage_{post_type}_posts_custom_column. Update the {post_type} parts below to match your custom post type.

add_filter( 'manage_{post_type}_posts_columns', 'set_custom_edit_book_columns' );
function set_custom_edit_book_columns($columns) {
  $columns[custom_field_status_metakey()] = __( 'Status', 'textdomain' ); // reorder columns, if needed
  return $columns;
}

add_action( 'manage_{post_type}_posts_custom_column' , 'custom_book_column', 10, 2 );
function custom_book_column( $column, $post_id ) {
  $key = custom_field_status_metakey();
  if ( $key === $column ) {
    echo get_post_meta( $post_id, $key, true );
  }
}

Add a filter select to the posts lists with restrict_manage_posts.

add_action( 'restrict_manage_posts', 'filter_post_by_custom_field_status' , 10, 2);
function filter_post_by_custom_field_status( $post_type, $which ) {
  if ( my_custom_post_type() === $post_type ) {
    $meta_key = custom_field_status_metakey();
    $options = custom_field_status_options();

    echo "<select name="{$meta_key}" id='{$meta_key}' class="postform">";
    foreach ( $options as $value => $name ) {
      printf(
        '<option value="%1$s" %2$s>%3$s</option>',
        esc_attr($value),
        ( ( isset( $_GET[$meta_key] ) && ( $_GET[$meta_key] === $value ) ) ? ' selected="selected"' : '' ),
        esc_html($name)
      );
    }
    echo '</select>';
  }
}

Use parse_query to filter posts based on the selected status value.

add_filter( 'parse_query', 'filter_parse_query_custom_field_status' );
function filter_parse_query_custom_field_status( $query ){
  global $pagenow;

  $meta_key = custom_field_status_metakey();
  $valid_status = array_keys(custom_field_status_options());
  $status = (! empty($_GET[$meta_key]) && in_array($_GET[$meta_key],$valid_status)) ? $_GET[$meta_key] : '';

  if ( is_admin() && 'edit.php' === $pagenow && isset($_GET['post_type']) && my_custom_post_type() === $_GET['post_type'] && $status ) {
    $query->query_vars['meta_key'] = $meta_key;
    $query->query_vars['meta_value'] = $status;
  }
}

Toss the codes above to your functions.php and modify as needed.


Or register a custom taxonomy for your post type and you’ll get the above kind of automatically. Plus using a custom taxonomy instead of meta can make post queries more efficient. More on that here https://tomjn.com/2016/12/05/post-meta-abuse/ by WPSE member Tom J Nowell.