I just set up a custom post type, mostly following this tutorial.

I'm setting up things on my site now, doing a lot of CSS modifications and such, and every few minutes the post just forgets everything that's in the custom meta boxes.

I'm sure I've done something wrong in the add_action/update_post_meta section, but I don't know what it is, because I don't really understand the entirety of what the code does.

In any case, here's the entire thing:

add_action('init', 'portfolio_register');

function portfolio_register() {

    $labels = array(
        'name' => _x('My Portfolio', 'post type general name'),
        'singular_name' => _x('Portfolio Item', 'post type singular name'),
        'add_new' => _x('Add New', 'portfolio item'),
        'add_new_item' => __('Add New Portfolio Item'),
        'edit_item' => __('Edit Portfolio Item'),
        'new_item' => __('New Portfolio Item'),
        'view_item' => __('View Portfolio Item'),
        'search_items' => __('Search Portfolio'),
        'not_found' =>  __('Nothing found'),
        'not_found_in_trash' => __('Nothing found in Trash'),
        'parent_item_colon' => ''

    $args = array(
        'labels' => $labels,
        'public' => true,
        'publicly_queryable' => true,
        'show_ui' => true,
        'query_var' => true,
        'menu_icon' => get_stylesheet_directory_uri() . '/article16.png',
        'rewrite' => true,
        'capability_type' => 'post',
        'hierarchical' => false,
        'menu_position' => null,
        'supports' => array('title','editor','thumbnail')

    register_post_type( 'portfolio' , $args );

register_taxonomy("portfolio_cat", array("portfolio"), array("hierarchical" => true, "label" => "Categories", "singular_label" => "Category", "rewrite" => true));

add_action("admin_init", "admin_init");

function admin_init(){
  add_meta_box("media_meta", "Media", "media_meta", "portfolio", "normal", "low");
  add_meta_box("highlights_meta", "Highlights", "highlights_meta", "portfolio", "normal", "low");
  add_meta_box("main_meta", "Main", "main_meta", "portfolio", "normal", "low");
  add_meta_box("tabbed_meta", "Tabbed", "tabbed_meta", "portfolio", "normal", "low");

function media_meta(){
  global $post;
  $custom = get_post_custom($post->ID);
  $media_meta = $custom["media_meta"][0];
  <p><label>Media:</label><br />
  <textarea cols="110" rows="5" name="media_meta"><?php echo $media_meta; ?></textarea></p>

function highlights_meta(){
  global $post;
  $custom = get_post_custom($post->ID);
  $highlights_meta = $custom["highlights_meta"][0];
  <p><label>Highlights:</label><br />
  <textarea cols="110" rows="5" name="highlights_meta"><?php echo $highlights_meta; ?></textarea></p>

function main_meta(){
  global $post;
  $custom = get_post_custom($post->ID);
  $main_meta = $custom["main_meta"][0];
  <p><label>Main:</label><br />
  <textarea cols="110" rows="5" name="main_meta"><?php echo $main_meta; ?></textarea></p>

function tabbed_meta(){
  global $post;
  $custom = get_post_custom($post->ID);
  $tabbed_meta = $custom["tabbed_meta"][0];
  <p><label>Tabbed:</label><br />
  <textarea cols="110" rows="5" name="tabbed_meta"><?php echo $tabbed_meta; ?></textarea></p>

add_action('save_post', 'save_details');

function save_details(){
  global $post;

  update_post_meta($post->ID, "media_meta", $_POST["media_meta"]);
  update_post_meta($post->ID, "highlights_meta", $_POST["highlights_meta"]);
  update_post_meta($post->ID, "main_meta", $_POST["main_meta"]);
  update_post_meta($post->ID, "tabbed_meta", $_POST["tabbed_meta"]);

add_action("manage_posts_custom_column",  "portfolio_custom_columns");
add_filter("manage_edit-portfolio_columns", "portfolio_edit_columns");

function portfolio_edit_columns($columns){
  $columns = array(
    "cb" => "<input type=\"checkbox\" />",
    "title" => "Portfolio Title",
    "description" => "Description",
    "media_meta" => "Media",
    "highlights_meta" => "Highlights",
    "main_meta" => "Main",
    "tabbed_meta" => "Tabbed",
    "portfolio_cat" => "Portfolio Categories"

  return $columns;
function portfolio_custom_columns($column){
  global $post;

  switch ($column) {
    case "description":
    case "media_meta":
      $custom = get_post_custom();
      echo $custom["media_meta"][0];
    case "highlights_meta":
      $custom = get_post_custom();
      echo $custom["highlights_meta"][0];
    case "main_meta":
      $custom = get_post_custom();
      echo $custom["main_meta"][0];
    case "tabbed_meta":
      $custom = get_post_custom();
      echo $custom["tabbed_meta"][0];
    case "portfolio_cat":
      echo get_the_term_list($post->ID, 'portfolio_cat', '', ', ','');
  • 1
    I don't have time to sift through your code for an answer (sorry), but I'd like to point out that you really should consider using a better naming scheme, functions like admin_init() have a REALLY high possibility of conflicting with other core or plugin functions. – mor7ifer Feb 14 '12 at 2:05
  • Oh, that’s a really terrible tutorial. So many errors. – fuxia Feb 14 '12 at 3:25
  • Yeah, agreed on both counts. It's a bad tutorial. – amphibious Feb 14 '12 at 4:19

In the function hooked onto save_posts you should make sure that you check that the action wasn't triggered by an auto-save routine. I suspect that the reason why the post 'forgets' the data is that the post auto-saves, and updates the post-meta with blank data.

To do this:

function save_details($post_id){
    //Make sure you check this isn't an autosave.
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )

    update_post_meta($post_id, "media_meta", $_POST["media_meta"]);
    update_post_meta($post_id, "highlights_meta", $_POST["highlights_meta"]);
    update_post_meta($post_id, "main_meta", $_POST["main_meta"]);
    update_post_meta($post_id, "tabbed_meta", $_POST["tabbed_meta"]);

I've had a quick glance at the tutorial. It seems to ignore best practice and security checks such as the following:

  1. The function hooked on save_posts is given the argument $post_id (the post ID). You should use this rather than global $post.
  2. Prefix your functions. save_details is very generic, and if the WordPress core or another plug-in uses that function name, it'll crash your blog. This is particularly important for plugin developers. Prefix it with something unique: my_name_save_details. Same goes for the other functions.
  3. Check user permissions. Use current_user_can to verify that a user is allowed to be editing the post.
  4. Uses nonces. In your metabox, add a nonce and verify it in my_name_save_details. This checks the data you're adding to your database really came from your metabox.(See Codex)
  5. Perform other checks. save_posts fires every time a post or page is saved/updated, but you only really want to be altering posts of your custom post type. It's not essential, but removes the possibility of upsets further down the line. You can use get_post_type to help with this.
  • A side note: you can include multiple fields inside a metabox. I'd recommend taking a look at this Codex page for a 'best practise' example (it's not a tutorial though...). Hope this helps :D – Stephen Harris Feb 14 '12 at 2:37
  • For a minimal example see my basic meta box. – fuxia Feb 14 '12 at 3:22

The answer from Stephen Harris is excellent, but shows that rolling your own friendly custom fields can be a real pain. To do things securely and robustly you have to hold WordPress' hand quite a bit. I'll save my rant about how WordPress should do many of these things by default. Instead, I'll point you to the Advanced Custom Fields plugin which will help with everything in the above question.

The plugin has nice documentation for each of the different types of fields provided, and handy code snippets for getting the data out and into your templates.

Best of all, it's free for everything you're trying to use it for.

This plugin isn't perfect for every site, but it's great for when you just want to add a few extra fields to a custom post type.

If you're interested in continuing with rolling your own friendly custom fields, I highly recommend this article from .net magazine. It walks through the issues you're experiencing, and has helped me in the past.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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