2

I have a plugin that creates a custom post type that has a custom field generated with the plugin. The custom field is stored in the database as a meta key called status. The value of this meta key is either "open" or "closed".

On the edit.php page that lists the posts, I'd like to display the contents of the status field in a column. I'd also like to add a filter that allows the admin to show the all the posts by default with the option to view only "open" and "closed" posts using the filter selector.

1 Answer 1

4

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.

2
  • Thanks, I was able to piece this together yesterday with more googling except for the parse_query filter. I posted another question about that here: wordpress.stackexchange.com/questions/351443/…. I have a meta_key with multiple "sub keys" for lack of a better word.
    – StevieD
    Oct 29, 2019 at 13:58
  • Great answer, thank you! 👌 Sep 2, 2022 at 11:14

Your Answer

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

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