2

My question is partly based on this answer: https://wordpress.stackexchange.com/a/5778/27135

Mike's answer is really great, and the solution worked fine until I needed to include my meta box in a custom post type. I tried to alter the code until that worked, but I did not succeed. I also do not need more than one selection meta box per page (Mike's code adds 3).

So I started from scratch, stripping away everything. That works up until the point where I need to save the data in my form.

My main problem right now is that I don't know how to save the currently selected option-tag when I Publish/Save. I'm not sure which data I should grab in the option tags and what to input to the update_post_meta()

I also tried creating my own loop using query_posts(), hoping to gain more control over what data I needed to save.. but no luck there either.

The code below is a very simplistic version of Mike's code. My goal was to strip it so that it only contains the essentials, ironically I must have stripped it too much and can't figure out what. I'd like to know what I need to add in order to save the current selection. :)

add_action( 'add_meta_boxes', 'select_box_add_meta_box' );
add_action( 'save_post', 'select_box_save_postdata' );

// Create the meta box
function select_box_add_meta_box() {
      add_meta_box(
          'select_item',
          'Select Item',
          'select_box_content',
          'custom_post_type'
      );
}

// Create the meta box content
function select_box_content() {
    $static_args = array(
        'post_type' => 'another_custom_post_type', 
        'show_option_none' => 'Choose item'
    );
    $selected_item = get_post_meta($post->ID,'_selected_item', true);
    for($i=0; $i<=2; $i++) {
        $incrementing_args = array(
            'id' => "selected_item_{$i}",
            'name' => 'selected_item[]',
            'selected' => (empty($selected_item[$i]) ? 0 : $selected_item[$i]),
        );
    }   

    wp_dropdown_pages(array_merge($static_args,$incrementing_args));


}

// Save the selection
function select_box_save_postdata($data, $postarr) {    
    update_post_meta($postarr['ID'], '_selected_item', $postarr['selected_item']);
    return $data;
}
2
  • Hi @joakimtonnesen: Reviewing your code and your question it's not clear at all what you are trying to accomplish. You describe the steps you've taken but you haven't described your desired outcome and since the code you've written is broken enough I can't guess what you are trying to do. For example, the wp_dropdown_pages() is inside the for() loop but that might not give you what you were trying to do. Feb 10, 2013 at 2:46
  • Thanks for the reply Mike! I will try to clarify my desired outcome: I want a meta box similar to your LittleBoxes class. The difference is that I need to include it on custom post types, and I only need one meta box (not 3 like yours). Also I'd like to do this with as little code as possible so I can understand it and modify it later, which is why I started from scratch. As for the for loop: I thought I needed it to ID my option tags. I removed it in a later version, but I am still not able to save the data. Feb 10, 2013 at 9:58

2 Answers 2

4

I believe this is what you were looking for.

I moved back to using a class because what you were using would very easily conflict with someone else's code who decided to use the function names select_box_add_meta_box(), select_box_content() and/or select_box_save_postdata(), which is reasonably likely. The class name WPSE_85107 is only likely to conflict with somebody else answering this question! Of course I'd suggest choosing a better name for your actual plugin but at least pick something that is unlike to conflict.

Using a class also allows us to use short and clear method names that (typically) can just match the hook name. This makes both writing and understanding code so much easier IMO. It is a little more complex when you use add_action() but you only have to learn how once:

add_action( 'add_meta_boxes', 'select_box_add_meta_box' );

vs:

add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );

And you were misusing 'save_post'; it needs two parameters which is the reason there is a 2 after the 10 where the 10 is simply the default priority and is only needed because we had to specify the number of parameters:

add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );

The correct parameters for save_post() are this:

function save_post( $post_id, $post )

vs. this:

function select_box_save_postdata($data, $postarr) 

Also in the save_post() method I tested to make sure it's the post type we care about and that there is indeed there is a valid element of $_POST['selected_post'] which should contain the post ID that was selected.

I chose to create (pseudo-)constants and instance variables so that it would be easier for you to see what parameters are required for the various functions such as add_meta_box() and wp_dropdown_pages(). I didn't add comments because I think if I did you'd see it as being more complicated and thus overwhelming like the other example.

Note that the wp_dropdown_pages() requires a 'hierarchical' post type which is why I had to hack it here to get it to list $post_type=='post'. The test site I used to test this example only has 'post' and 'page' post types; you should be able to change the two lines with $FOR_POST_TYPE and $SELECT_POST_TYPE to get the post types you want.

I used the translation function __() for the labels in case you are creating a plugin that ever needs to work in other languages.

I used admin_init() so that the other code is only run when in the admin but not on the front end. It's not a big deal but every little bit helps.

And that's about it. Here's the code:

<?php
/**
 * Plugin Name: @WPSE 85107
 * Description: <a target="_blank" href="http://wordpress.stackexchange.com/q/85107/89">WPSE 85107</a>
 */

class WPSE_85107 {
  var $FOR_POST_TYPE = 'page';
  var $SELECT_POST_TYPE = 'post';
  var $SELECT_POST_LABEL = 'Post';
  var $box_id;
  var $box_label;
  var $field_id;
  var $field_label;
  var $field_name;
  var $meta_key;
  function __construct() {
    add_action( 'admin_init', array( $this, 'admin_init' ) );
  }
  function admin_init() {
    add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
    add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
    $this->meta_key     = "_selected_{$this->SELECT_POST_TYPE}";
    $this->box_id       = "select-{$this->SELECT_POST_TYPE}-metabox";
    $this->field_id     = "selected-{$this->SELECT_POST_TYPE}";
    $this->field_name   = "selected_{$this->SELECT_POST_TYPE}";
    $this->box_label    = __( "Select {$this->SELECT_POST_LABEL}", 'wpse-85107' );
    $this->field_label  = __( "Choose {$this->SELECT_POST_LABEL}", 'wpse-85107' );
  }
  function add_meta_boxes() {
    add_meta_box(
      $this->box_id,
      $this->box_label,
      array( $this, 'select_box' ),
      $this->FOR_POST_TYPE,
      'side'
    );
  }
  function select_box( $post ) {
    $selected_post_id = get_post_meta( $post->ID, $this->meta_key, true );
    global $wp_post_types;
    $save_hierarchical = $wp_post_types[$this->SELECT_POST_TYPE]->hierarchical;
    $wp_post_types[$this->SELECT_POST_TYPE]->hierarchical = true;
    wp_dropdown_pages( array(
      'id' => $this->field_id,
      'name' => $this->field_name,
      'selected' => empty( $selected_post_id ) ? 0 : $selected_post_id,
      'post_type' => $this->SELECT_POST_TYPE,
      'show_option_none' => $this->field_label,
    ));
    $wp_post_types[$this->SELECT_POST_TYPE]->hierarchical = $save_hierarchical;
  }
  function save_post( $post_id, $post ) {
    if ( $post->post_type == $this->FOR_POST_TYPE && isset( $_POST[$this->field_name] ) ) {
      update_post_meta( $post_id, $this->meta_key, $_POST[$this->field_name] );
    }
  }
}
new WPSE_85107();

I know you wanted it to be simple so you could understand it but this is pretty much the bare minimum you want for your plugin given the functionality you are asking for. Anything less and you will be cutting off meat and not fat.

What I did not do was add in several of the features that I think are really mandatory if you are building a plugin for someone else to use. But you can read about those features here.

1
  • Simply beautiful, Mike. Works really well! Thanks for your help. :) Feb 10, 2013 at 19:28
1

The save_post action will call your function and send it the $post_id, not $data and $postarr.

Your select_box_save_postdata function needs to accept $post_id as its only parameter:

add_action( 'save_post', 'select_box_save_postdata' );

function select_box_save_postdata( $post_id ) {
    $selected_item = null;

    // your form data is in the $_POST array
    if ( isset( $_POST['selected_item'] ) && $_POST['selected_item'] ) {
        $selected_item = $_POST['selected_item'];
    }

    // then use the $post_id to save your post meta
    update_post_meta( $post_id, '_selected_item', $selected_item );
}

See codex page for save_post for more info.

2
  • Thanks for the answer, jevets. That helped me get the correct update_post_meta, but I think my $_POST array is empty. Do I need to add something to make sure my form data goes to the $_POST array? // Create the meta box content function select_box_content() { $selected_item = get_post_meta( get_the_ID(), '_selected_item', true ); $static_args = array( 'post_type' => 'partnerbedrift', 'show_option_none' => 'Choose item' ); wp_dropdown_pages($static_args); } Feb 9, 2013 at 12:29
  • Yeah my main problem now is that $_POST['selected_item'] is empty. So I need to get the selected option's value into the $_POST arra. Then I should be very close! I would edit the above post but there was a 5 minute time limit. Feb 9, 2013 at 14:00

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.