Add Custom Post Type Columns

One of the exciting new features in WordPress 3.0 is custom post types. We can create our own post types by using the register_post_type function.

If we enable UIs for our custom post type, we will get additional menu items on our WordPress dashboard similar to the Edit and Add New options for standard posts.

Here, we consider how to add new columns to the Edit custom post type screen.

1. Create a Custom Post Type

Creating a custom post type is surprisingly straight-forward and well documented in the WordPress Codex.

In this example, we create a custom post type called gallery.

	$labels = array(
		'name' => _x('Galleries', 'post type general name'),
		'singular_name' => _x('Gallery', 'post type singular name'),
		'add_new' => _x('Add New', 'gallery'),
		'add_new_item' => __("Add New Gallery"),
		'edit_item' => __("Edit Gallery"),
		'new_item' => __("New Gallery"),
		'view_item' => __("View Gallery"),
		'search_items' => __("Search Gallery"),
		'not_found' =>  __('No galleries found'),
		'not_found_in_trash' => __('No galleries found in Trash'), 
		'parent_item_colon' => ''
	  );
	  $args = array(
		'labels' => $labels,
		'public' => true,
		'publicly_queryable' => true,
		'show_ui' => true, 
		'query_var' => true,
		'rewrite' => true,
		'capability_type' => 'post',
		'hierarchical' => false,
		'menu_position' => null,
		'supports' => array('title','thumbnail','excerpt')
	  ); 
	  register_post_type('gallery',$args);

2. Add New Custom Post Type Columns

As shown in the screen-shot above, our custom post type Edit screen (called Gallery) starts off with four columns – checkbox, Title, Author, and Date.

To add new columns to our Edit screen, we want to hook into the manage_$pagename_columns filter. The $pagename of the Edit screen is edit-$post_type.

Therefore, in this example, –

  • Edit page name = edit-gallery
  • Add column filter hook = manage_edit-gallery_columns

Our add column function call looks like this –

// Add to admin_init function
add_filter('manage_edit-gallery_columns', 'add_new_gallery_columns');

Our filter function accepts an array of column names, and returns our new column array once we are done.

	function add_new_gallery_columns($gallery_columns) {
		$new_columns['cb'] = '<input type="checkbox" />';
		
		$new_columns['id'] = __('ID');
		$new_columns['title'] = _x('Gallery Name', 'column name');
		$new_columns['images'] = __('Images');
		$new_columns['author'] = __('Author');
		
		$new_columns['categories'] = __('Categories');
		$new_columns['tags'] = __('Tags');
	
		$new_columns['date'] = _x('Date', 'column name');
	
		return $new_columns;
	}

In the example above we fully replace the column array with our own entries. We can also just add columns by adding new elements into the existing $gallery_columns array. However, our added columns will only appear after the existing default columns.

The functions above will add new columns into our Edit Gallery screen which now looks like this –

If we want the column to be sortable, then we can use the manage_{$screen->id}_sortable_columns filter. Here is a good example for making sortable columns.

3. Render Our New Custom Post Type Columns

Note – In the screen-shot above, the ID and Images columns are empty because they are not standard WordPress post columns. Standard WordPress post columns include –

  • ‘cb’ – Post checkbox.
  • ‘date’ – Date when post was last modified.
  • ‘title’ – Post title and common post actions including Edit, Quick Edit, Trash, and View.
  • ‘categories’ – Post categories.
  • ‘tags’ – Post tags.
  • ‘comments’ – Number of post comments.
  • ‘author’ – Post author.

To render our new columns, ‘id’ and ‘images’, we must hook into the manage_posts_custom_column action (in WordPress 3.0).

In WordPress 3.1, we want to hook into the manage_{$post_type}_posts_custom_column action. Since our example post type is gallery, we want to hook into manage_gallery_posts_custom_column.

        // Add to admin_init function
	add_action('manage_gallery_posts_custom_column', 'manage_gallery_columns', 10, 2);

	function manage_gallery_columns($column_name, $id) {
		global $wpdb;
		switch ($column_name) {
		case 'id':
			echo $id;
		        break;

		case 'images':
			// Get number of images in gallery
			$num_images = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_parent = %d;", $id));
			echo $num_images; 
			break;
		default:
			break;
		} // end switch
	}	

Our ‘id’ and ‘images’ columns will now be rendered with the proper values.

We Are Done!

Another interesting action hook in the Edit screen page is restrict_manage_posts which allows us to control which custom post objects we want to show on the page.

And just like that … we are done!

Related Articles

Comments

  1. Dipankar Barman says

    Hi Author!

    It is a awesome tutorial. it help me lot to make custom post type in my site.
    thanks for your code. it’s working fine.

  2. Momo says

    i use this code don’t display ma customs posts categoriers please helpe me
    //’manage_career_posts_custom_column’,
    add_filter(‘manage_career_posts_custom_column’,’add_new_career_columns’);
    function add_new_career_columns($career_columns) {
    $new_columns['title'] = _x(‘Title post’);
    $new_columns['author'] = __(‘Author’);
    $new_columns['categories'] = __(‘Categories’);
    $new_columns['tags'] = __(‘Tags’);
    $new_columns['date'] = _x(‘Date’);
    return $new_columns;
    }

    • says

      I apologize my script tags were being parsed as html. here is the code again

      switch ($column_name) {
      case ‘images':
      $thumnail_id = get_post_thumbnail_id($id);
      $imgsrc = wp_get_attachment_image_src($thumnail_id, ‘thumbnail’);

      if ( has_post_thumbnail($id) ) {
      echo ”;
      ;
      }

      break;
      default:
      break;
      }

  3. Danyo says

    Great Tutorial!

    How would i go about making the column names clickable to order by asc or desc like you would on the standard post screen?

    Thanks, Danyo

  4. says

    If we make a custom post type, so we also may have a different field to be edited. How to insert custom field (may be, not as postmeta but taken from different table) in edit post page?
    ie in: /wp-admin/post.php?post=xx&action=edit

  5. Erik F says

    Wonderful, this solves many of my problems. However, I have a question, how do I show the galleries for the visitor? Is it through single.php?

    • Erik F says

      When I try to look at my new gallery, I see only what’s in 404.php. What am I doing wrong? Is there any way I can list all the galleries on one page (such as posts)?

      • says

        When you say new gallery, are you using the WordPress native gallery or the gallery object from Shiba Media Library? When you edit the gallery in the dashboard, do all the images show up? What do you have your gallery permalink set to?

  6. says

    Thanx , your post is so great. But i have a Q related to categories columns. It is all time (uncategorized) even if the custom type post categorized !!

    How i can solve this ?!

  7. says

    Hey, nice article! I am planning to create custom post types for Restaurants with menus and others things. I am unsure of having one CPT or many; say one for Restaurant, menus and later Location.

    Thank you!

  8. Greg says

    Great post — it’s unbelievable the amount of Googling I had to do to find a satisfactory (and clear!) method to do this. Thanks for letting me end my search!

  9. wp_harish says

    Hey thanks very much..
    I am eager to know how Can we add functionality to custom column for sorting of posts…just like Title and Date columns come by default..

  10. says

    Here’s what I did to insert two columns in the middle of a custom post type table … which is also more extensible, as it lets other plugins do the same:

    
    	function add_new_columns( $cols ){
    		return array_merge(
    			array_slice( $cols, 0, 2 ),
    			array( 	'subtitle' => __('Subtitle'),
    					'pub_date' => __('Publication Date') ),
    			array_slice( $cols, 2 )
    		);
    	}
    

    If you wanted to be even pickier, instead of hardcoding `2` for after the second column, you could use something to find the index of the associative key, either directly or by snatching an array of the keys and searching in there with array_keys();

  11. says

    great tutorial!
    i have a question: i want to let the user order post by a custom field value, insted of simply show the value of that custom field.
    Thank you

  12. Nuwanda says

    Ok, this sorta works for my custom post type.

    But I have a custom taxomony instead of the normal wordpress categories.

    How do I show the custom taxonomy that the post belongs to?

    If my custom taxonomy is Cars, I’m using…

    $new_columns['cars'] = __(‘Car’);

    ..but all I’m getting is the header and no taxonomy listed.

    • ShibaShake says

      You need to fill in the column values yourself by hooking into the manage_{$post_type}_posts_custom_column action.

      Check out section 3 in the article above.

      • Trewknowledge says

        I am also having a problem displaying the right category. I have a custom post called “slider” so I wrote out
        manage_slider_posts_custom_column

        However all of the posts say “Uncategorized”. All of the posts have a “Slider Category” associated with it.

        Any thoughts?

        • ShibaShake says

          It is difficult to say without looking at the code.

          First, I would just do an “echo blah” to see if your manage_slider_posts_custom_column function is being called. If the function is being called, then likely, there is some bug while extracting the category values from the slider objects.

  13. Dan says

    Hi, I just used part of this code to add thumbnails to my custom post types. I used get_the_post_thumbnail($post-ID,array(100,100)); in the switch part for a column I made called image. Worked an absolute treat.

    Epic post, thanks.

  14. says

    Thanks for this post, it has been very enlightening. However i have got just one challenge using this feature. I have a custom post type for events and i use this code (below) to create custom columns:

    function add_new_events_columns($event_columns) {
    $new_columns['cb'] = ”;
    $new_columns['title'] = _x(‘Event Name’, ‘column name’);
    $new_columns['location'] = _x(‘Event Location’, ‘column name’);
    $new_columns['events_date'] = _x(‘Event Date’, ‘column name’);
    $new_columns['date'] = _x(‘Date Posted’, ‘column name’);
    return $new_columns;
    }

    The columns show perfectly well. However, when i tried to call out the content of the location meta box using this code:

    function manage_events_columns($column_name, $id) {
    global $wpdb;
    switch ($column_name) {
    case ‘location':
    $location_meta = get_post_meta($post->id, ‘location’, true);
    echo $location_meta;
    break;
    }
    }

    I can’t seem to use this method to get the content of the meta boxes to show in the respective columns. Please, i need help on this.

  15. Matt says

    Is it possible to add custom meta field values to the columns? I would like to set _lastname, _firstname as the title. Thanks Shibashake!

  16. Joe says

    Just confirmed: everything is fine in 3.1 RC2 except that If the custom post type is registered as ‘hierarchical’ => true, then the column will not populate with the data.

    • Kcssm says

      Yes I also got the same problem. The columns doesnot get populated when the `‘hierarchical’ => true` is set. Looking for a solution.

      Thanks..

      • ShibaShake says

        For hierarchical custom post types in WP 3.1 try using –
        add_action(‘manage_pages_custom_column’, ‘manage_gallery_columns’, 10, 2);

        or you can also use –
        add_action(“manage_{$post->post_type}_posts_custom_column”, ‘manage_gallery_columns’, 10, 2);

        • says

          in WP 3.0.4, this is also the solution to this problem.

          basically, if your custom post type IS hierarchical, the action hook is “manage_pages_custom_column”. if you did not specificy “hierarchical” => true in the register_post_type , it is by default FALSE, and you can hook to custom columns via the “manage_posts_custom_column” as shown in this post

          good times

  17. says

    I was also wondering what’s the difference between:

    $new_columns['title'] = _x(‘Gallery Name’, ‘column name’);

    and

    $new_columns['categories'] = __(‘Categories’);

    What does ‘column name’ when specified?

    Thanks.

  18. says

    Thanks for the great article, so hard to find tutorials on this topic!

    I have 2 post types: ‘Portfolio’ (with meta_data) and ‘Client’. I’m adding custom columns to ‘Portfolio’ admin edit page:

    add_filter(‘manage_edit-portfolio_columns’, ‘add_new_portfolio_columns’);
    function add_new_portfolio_columns($portfolio_columns) {
    $new_columns['cb'] = ”;

    $new_columns['title'] = _x(‘Portfolio Title’, ‘column name’);
    $new_columns['portfolio_types'] = __(‘Portfolio Types’); //Portfolio taxonomy
    $new_columns['client'] = __(‘Client’); //reference Client post type via ‘client_id’ in Portfolio meta
    $new_columns['testimonial'] = __(‘Testimonial’); //Portfolio meta into
    $new_columns['date'] = _x(‘Date’, ‘column name’);

    return $new_columns;
    }

    I have a problem that I can’t understand. I’ve specified my custom columns, all good, and here is the method that I use to populate the columns. It seems that after extracting ‘client_id’ from ‘Portfolio’ meta and finding ‘Client’ post type things go wrong. Because for the next column I’m exctracting ‘testimonial’ from ‘Portfolio’ meta, but it seems that $post is now a ‘Client’ post.

    add_action(‘manage_posts_custom_column’, ‘manage_portfolio_columns’, 10, 2);
    function manage_portfolio_columns($column_name, $post_id) {
    global $wpdb, $post, $key;

    switch ($column_name){
    // PORTFOLIO post type columns
    case ‘portfolio_types':
    $terms = get_the_term_list($post->ID , ‘portfolio_type’ , ” , ‘, ‘ , ” );
    if(is_string($terms)) {
    echo $terms;
    }
    break;
    case ‘client':
    $portfolio_meta = get_post_meta($post->ID, $key, true);
    $client_id = $portfolio_meta['client_id'];
    // client specified – display his name
    if($client_id){
    $client = new WP_Query(“post_type=client&p=$client_id”);
    //this loop seems to scrue up the value of $post for the following column… instead of ‘Portfolio’ post type, I somehow getting ‘Client’ and I dont understand why
    if($client->have_posts()) { while($client->have_posts()) { $client->the_post();
    the_title();
    }
    }
    }
    break;
    case ‘testimonial':
    $portfolio_meta = get_post_meta($post->ID, $key, true);
    echo $post_id;
    $testimonial = $portfolio_meta['testmonial'];
    if($testimonial){
    echo $testimonial;
    }
    break;
    default:
    break;
    }
    }

    Would really appreciate any help!
    Thanks.

    • ShibaShake says

      Do you have a column named client for your Client post type as well? If so, there could be problems.

      The manage_posts_custom_column gets called while rendering *all* post type objects including posts, pages, and custom post types. You could do a post_type check in the beginning of the function to limit the scope of its operation.

  19. says

    This is the code I used:

    First adding this to function.php:
    add_theme_support(‘post-thumbnails’);
    add_image_size( ‘release-post-thumbnail’, 95, 95 ); //Custom Thumbnail size

    Then this is my case:
    case “cover”:
    $custom = get_post_custom();
    echo the_post_thumbnail( ‘release-post-thumbnail’ );
    break;

    • ShibaShake says

      You can hook into the wp loop query request to do this. I usually hook into the ‘request’ filter hook. However, you must be careful with this because it will get run every time the WP loop (i.e., the wp function) gets called. Put in tight conditionals so that your orderby property only gets applied on the relevant pages.

  20. says

    Hi Shibashake,

    is there’s a way to remove the integrated “categories” column to add the same one (categories) but with categories ordered by hierarchy (respecting the tree of categories) ?

    And, if yes, how to do that ?

    TIA.

    Amicably,

    Pierre.

    • ShibaShake says

      To remove a column, in add_new_gallery_columns you would just

      unset($gallery_columns['categories']);
      

      Then you would add your own column –

      $gallery_columns['my_super_categories'] = __('Categories');
      

      Finally in the manage_gallery_columns you would add your own category tree rendering function –

      ...
      		switch ($column_name) {
      		case 'my_super_categories':
      			// render category tree
                              break;
      ...
      
  21. says

    Hi Shibashake,

    i’m really new in the discovery of Custom types and this is second time i found your website as a REALLY good ressource :) !
    You help me to found what i was searching for.

    So, with this comment, i wished to thank you :).

    PS. Do you know a way to add our custom(s) column(s) in a particular position ?

    Amicably,

    Pierre.

    • ShibaShake says

      Do you know a way to add our custom(s) column(s) in a particular position

      You will need to insert it at exactly that location in the array. I usually just create a new array, but you can also use php array operations e.g. array_splice, array_merge, array_push to achieve an element insert.

      • Maurizio says

        array_splice doesn’t work with associative arrays. You can add your custom column at a chosen position defining the key before one you want your custom column to appear. For instance, if you want to add the custom column ‘Status’ before ‘column author’ you can do like this:

        your_filter_function($columns) {
        $insert_before = ‘author';
        foreach ($posts_columns as $key => $val) {
        if ($key == $insert_before) {
        $new_columns['status'] = ‘Status';
        $new_columns[$key] = $val;
        } else {
        $new_columns[$key] = $val;
        }
        }
        return($new_columns);
        }

  22. alex says

    Is there a way to create a filter like in the edit posts category filter?

    For example a filter by a custom field?

    Thanks a lot, your lasts post are amazing!

    • ShibaShake says

      Hello Alex,
      So you want to create a filter for each of your custom fields?

      I would look into adding the apply_filters command and pass in the value(s) you want to modify. For example, if I want to modify the num_images value I could do the following –

      ...
      	case 'images':
      		// Get number of images in gallery
      		$num_images = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_parent = {$id};"));
                      $num_images = apply_filters('shiba_num_images_filter', $num_images);
      		echo $num_images; 
      		break;
      ...
      // add filter function
      add_filter('shiba_num_images_filter', 'my_filter_function');
      
    • ShibaShake says

      Thanks Lane. In terms of donation, just link to us when appropriate and that would be more than enough. :)

    • says

      Ahh, this is exactly what I’m trying to accomplish. How do you apply an image to the custom_columns function? I have a custom field value as the image url, I just need to pass that into the custom_columns function to display it on the cpt’s edit page. The search continues!

      • ShibaShake says

        Just do somethiing like –

        case 'my_image_field' :
        echo "<img src='{$my_image_url}' width='100' height='100'/>";
        break;
        
        • says

          Thank you Shiba for such a quick response. I’m still trying to figure this out.
          In place of $my_image_url how would I attach the custom field of “tuticon” to it?
          Basically want an image in place of the tutorial icon file names.

        • says

          I must be writing this completely wrong. Echoing out the custom field might not be the way to do this? I apologize for my lack of php knowledge, such a noob I know. I greatly appreciate your help!!

          	case 'my_image_field':
          		$my_image_field="thumbnail";
          		echo "<img src=\"ID, $my_image_field, true); ?>\" />";
          		break;
          
          • ShibaShake says

            Glad it worked out Ruiz. I think some of the php got lost so my guess is you used wp_get_attachment_image?

            < ?php 
            echo wp_get_attachment_image( $attachment_id, $size, $icon ); 
            ?> 
            

            Is that right? I will update your comment if that is the case. Thanks.

          • says

            So what was your final answer to showing images from posts? I’ve been searching how to do this for days. I would love to see your final code!

Trackbacks

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>