4

I have multiple radio buttons set up inside of a metabox in a custom post type. I have created the metabox like so:

    add_action('admin_init', 'add_meta_boxes', 1);
    function add_meta_boxes() {
        add_meta_box( 'repeatable_fields', 'Top 10 Movie List', 'repeatable_meta_box_display', 'cpt_top_ten_list', 'normal', 'default');
    }

    function repeatable_meta_box_display() {
        global $post;
        $repeatable_fields = get_post_meta($post->ID, 'repeatable_fields', true);
        wp_nonce_field( 'repeatable_meta_box_nonce', 'repeatable_meta_box_nonce' );

     if ( $repeatable_fields ) :

    // set a variable so we can append it to each row
    $num = 0;
    $second_num = 0;

    foreach ( $repeatable_fields as $field ) {
    $num++;

<div class=" playbackformat-holder-<?php echo $num; ?> playbackformat-holder">
    <form id="playback-form-<?php echo $num; ?>">
        <label for="dvd-<?php echo $num; ?>">
        <input type="radio" class="playbackformat-holder-radiobutton" value="dvd" name="playback_format[<?php echo $second_num; ?>]" id="dvd-<?php echo $num; ?>" <?php if($field['playback_format'] == 'dvd') { echo 'checked'; } ?>>DVD
        </label>

        <label for="bluray-<?php echo $num; ?>">
        <input type="radio" class="playbackformat-holder-radiobutton" value="bluray" name="playback_format[<?php echo $second_num; ?>]" id="bluray-<?php echo $num; ?>" <?php if($field['playback_format'] == 'bluray') { echo 'checked'; } ?>>Bluray
        </label><br>    

        <label for="3d-<?php echo $num; ?>">
        <input type="radio" class="playbackformat-holder-radiobutton" value="3d" name="playback_format[<?php echo $second_num; ?>]" id="3d-<?php echo $num; ?>" <?php if($field['playback_format'] == '3d') { echo 'checked'; } ?>>3d
        </label><br />
    </form>     
</div>
second_num++;
}

And here is my save function to store the data into an array.

add_action('save_post', 'repeatable_meta_box_save', 10, 2);
function repeatable_meta_box_save($post_id) {

    if ( ! isset( $_POST['repeatable_meta_box_nonce'] ) ||
    !wp_verify_nonce( $_POST['repeatable_meta_box_nonce'], 'repeatable_meta_box_nonce' ) )
    return;

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
    return;

    if (!current_user_can('edit_post', $post_id))
    return;

    $old = get_post_meta($post_id, 'repeatable_fields', true);
    $new = array();

    $playbackFormats = $_POST['playback_format'];


    $count = count( $names ) - 1;

    for ( $i = 0; $i < $count; $i++ ) {

    // currently only storing the last stored radio value
    $new[$i]['playback_format'] = $playbackFormats[$i];

    // save movie description
     // and however you want to sanitize
    if ( !empty( $new ) && $new != $old ) {
        update_post_meta( $post_id, 'repeatable_fields', $new );
    } elseif ( empty($new) ) {
        delete_post_meta( $post_id, 'repeatable_fields', $old );
    }

}

The problem I am facing is that , lets say I have three rows. If I set the first to DVD, the second to bluray and the third to 3D only the last value stores as 3D. All other ['playback_format'] store as 'null'.

The way I have things set up it looks like things should be saving properly. This is a trimmed down version. I have input fields that are storing correctly, but the radio buttons have been a major issue.

It looks like my save function should be storing each ['playback_format'] into its appropriate place in the array. But only one stores. I've gotten it to store the same value for all three rows, but can't get a unique value for each row and not sure why.

Any help would be appreciated, as this is the last piece of the puzzle!

Thanks! (Screenshot below for a visual aid)

repeatable rows - visual aid

Here is the value of var_dump($field); after storing the data in the screenshot above.

array(4) { 
 ["name"]=> string(116) "http://onecinephile.staging.wpengine.com/wp-content/uploads/2014/01/lon-chaney-phantom-hunchback-penalty-300x280.jpg" 
 ["select"]=> string(14) "Test #2" 
 ["url"]=> string(35) "Testing movie description #2" 
 ["playback_format"]=> string(3) "dvd" 
} 

array(4) { 
 ["name"]=> string(103) "http://onecinephile.staging.wpengine.com/wp-content/uploads/2014/01/spencer-tracy-boys-town-300x225.jpg" 
 ["select"]=> string(14) "Test #1" 
 ["url"]=> string(35) "Testing movie description #1" 
 ["playback_format"]=> NULL 
} 

array(4) { 
 ["name"]=> string(100) "http://onecinephile.staging.wpengine.com/wp-content/uploads/2014/01/Ronald-and-Madeleine-225x300.jpg" ["select"]=> string(14) "Test #3
 ["url"]=> string(35) "Testing movie description #3" 
 ["playback_format"]=> NULL 
}
9
  • I don't understand what you mean by "rows"... do you have a screenshot of your metabox that might make it clearer? Also, have you ever tried WP_Alchemy? It makes complex metaboxes pretty simple to set up. Feb 21, 2014 at 16:05
  • I'm trying to avoid using WP_Alchemy. I've set up all of my data and fields, It would be too time consuming to go back and re-code it all. I know it's possible, but don't understand why its not storing correctly. I'll try and add in a screenshot
    – EHerman
    Feb 21, 2014 at 16:08
  • Image attached above. You can see each row is labeled with the appropriate row number to the left. All other fields , including text inputs and the WYSIWYG and image fields are all storing correctly.
    – EHerman
    Feb 21, 2014 at 16:13
  • In your display, can you var_dump() the data? I want to determine whether you are not saving correctly or just not displaying correctly. I've had the latter happen many times, especially because you aren't using checked() Feb 21, 2014 at 16:21
  • Sure, give me one second. I can tell you the first ['playback_format'] is storing, but the rest are not. I will post it below the screen shot
    – EHerman
    Feb 21, 2014 at 16:27

1 Answer 1

4

So I came back to this after practice and implemented some of the Alchemy setup, meaning all the inputs have names like name="_movies[3][title]" where 3 is the iteration we're on in the loop.

Create the Metabox

// metaboxes should be registered on the add_meta_boxes hook
add_action('add_meta_boxes', 'add_meta_boxes' );
function add_meta_boxes() {
    add_meta_box( 'repeatable_fields', 'Top 10 Movie List', 'repeatable_meta_box_display', 'cpt_top_ten_list', 'normal', 'default');
}


function repeatable_meta_box_display( $post ) { 

$repeatable_fields = get_post_meta($post->ID, 'repeatable_fields', true); 

if ( empty( $repeatable_fields ) ){
    $repeatable_fields[] = array ( 
                                'image' => '',
                                'title' => '',
                                'playback_format' => 'dvd',
                                'description' => '' );
}

wp_nonce_field( 'hhs_repeatable_meta_box_nonce', 'hhs_repeatable_meta_box_nonce' ); 
?> 

<table id="repeatable-fieldset-one" class="widefat fixed" cellspacing="0" style="width:100%;"> 
<thead> 
<tr> 
<th style="width:25px;" scope="col">Rank</th> 
<th style="width:170px;" scope="col">Image</th> 
<th width="145px" scope="col">Movie Title</th> 
<th width="300px" scope="col">Movie Description</th> 
<th width="8%" scope="col">Re-Order</th> 
</tr> 
</thead> 
<tbody> 


<?php 

// set a variable so we can append it to each row 
$i = 1; 

foreach ( $repeatable_fields as $field ) { ?>

<tr class="single-movie-row ui-state-default"> 

    <td> 
    <label for="_movies[<?php echo $i;?>][rank]"> 
    <input name="_movies[<?php echo $i;?>][rank]" id="_movies[<?php echo $i;?>][rank]" class="movie_rank_number" disabled="disabled" type="text" value="# <?php echo $i;?>" /> 
    </label>    
    </td> 

    <td> 
    <label for="_movies[<?php echo $i;?>][image]"> 
    <input name="_movies[<?php echo $i;?>][image]" class="upload_image" id="_movies[<?php echo $i;?>][image]" type="text" size="36" value="<?php echo esc_attr( $field['image'] );?>" /> 
    <input class="upload_image_button" id="_movies[<?php echo $i;?>][upload_image_button]" type="button" value="Upload Image" /> 
    </label> 
    </td> 

    <td> 
    <!-- title field --> 
    <textarea name="_movies[<?php echo $i;?>][title]" id="_movies[<?php echo $i;?>][title]" class="title_tinymce_editor"><?php echo esc_html( $field['title'] );?></textarea> 

    <div class="playbackformat-holder"> 

    <label for="_movies[<?php echo $i;?>][playback_format][dvd]"> 
    <input type="radio" id="_movies[<?php echo $i;?>][playback_format][dvd]" name="_movies[<?php echo $i;?>][playback_format]" value="dvd" <?php checked( $field['playback_format'], 'dvd' ); ?> />DVD 
    </label> 
    <label for="_movies[<?php echo $i;?>][playback_format][bluray]"> 
    <input type="radio" id="_movies[<?php echo $i;?>][playback_format][bluray]" name="_movies[<?php echo $i;?>][playback_format]" value="bluray" <?php checked( $field['playback_format'], 'bluray' ); ?> />Bluray 
    </label><br>    
    <label for="_movies[<?php echo $i;?>][playback_format][3d]"> 
    <input type="radio" id="_movies[<?php echo $i;?>][playback_format][3d]" name="_movies[<?php echo $i;?>][playback_format]" value="3d" <?php checked( $field['playback_format'], '3d' ); ?> />3d 
    </label><br />  

    </div> 

    </td> 

    <td>
    <textarea id="_movies[<?php echo $i;?>][description]" name="_movies[<?php echo $i;?>][description]" class="movie_description_editor_hidden"><?php echo esc_html( $field['description'] );?></textarea>
    </td> 

    <td>
    <a class="button remove-row" href="#">Remove Row</a><img src="<?php echo get_template_directory_uri() ?>/images/draggable-icon.png" alt="sortable icon" class="jQuerySortableIcon">
    </td> 

</tr> 

<?php $i++; } ?>


<!-- empty hidden one for jQuery --> 
<tr class="empty-row screen-reader-text single-movie-row"> 

    <td> 
    <label for="_movies[%s][rank]"> 
    <input name="_movies[%s][rank]" id="_movies[%s][rank]" class="movie_rank_number" disabled="disabled" type="text" value="" /> 
    </label>    
    </td> 

    <td> 
    <label for="_movies[%s][image]"> 
    <input name="_movies[%s][image]" class="upload_image" id="_movies[%s][image]" type="text" size="36" value="" /> 
    <input class="upload_image_button" id="_movies[<?php echo $i;?>][upload_image_button]" type="button" value="Upload Image" /> 
    </label> 
    </td> 

    <td> 
    <!-- title field --> 
    <textarea name="_movies[%s][title]" id="_movies[%s][title]" class="title_tinymce_editor"></textarea> 

    <div class="playbackformat-holder"> 

    <label for="_movies[%s][playback_format][dvd]"> 
    <input type="radio" id="_movies[%s][playback_format][dvd]" name="_movies[%s][playback_format]" value="dvd" <?php checked( 'dvd', 'dvd' ); ?> />DVD 
    </label> 
    <label for="_movies[%s][playback_format][bluray]"> 
    <input type="radio" id="_movies[%s][playback_format][bluray]" name="_movies[%s][playback_format]" value="bluray" />Bluray 
    </label><br>    
    <label for="_movies[%s][playback_format][3d]"> 
    <input type="radio" id="_movies[%s][playback_format][3d]" name="_movies[%s][playback_format]" value="3d" />3d 
    </label><br />  

    </div> 

    <!-- drop down or checkbox's with release formats --> 
    </td> 

    <td>
    <textarea id="_movies[%s][description]" name="_movies[%s][description]" class="movie_description_editor_hidden"></textarea>
    </td> 


    <td>
    <a class="button remove-row" href="#">Remove Row</a><img src="<?php echo get_template_directory_uri() ?>/images/draggable-icon.png" alt="sortable icon" class="jQuerySortableIcon">
    </td> 

</tr> 

</tbody> 
</table> 

<p id="add-row-p-holder"><a id="add-row" class="btn btn-small btn-success" href="#">Insert Another Row</a></p> 
<?php 
}

Save the Meta

Saving is now relatively easy because the arrays in the names sets it up for us:

add_action('save_post', 'hhs_repeatable_meta_box_save', 10, 2); 
function hhs_repeatable_meta_box_save($post_id) { 

if ( ! isset( $_POST['hhs_repeatable_meta_box_nonce'] ) || 
!wp_verify_nonce( $_POST['hhs_repeatable_meta_box_nonce'], 'hhs_repeatable_meta_box_nonce' ) ) 
return; 

if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) 
return; 

if (!current_user_can('edit_post', $post_id)) 
return; 

$clean = array(); 

if  ( isset ( $_POST['_movies'] ) && is_array( $_POST['_movies'] ) ) :

    foreach ( $_POST['_movies'] as $i => $movie ){

    // skip the hidden "to copy" div
    if( $i == '%s' ){ 
        continue;
    }

$playback_formats = array ( 'dvd', 'bluray', '3d' );

    $clean[] = array( 
        'image' => isset( $movie['image'] ) ? sanitize_text_field( $movie['image'] ) : null,
        'title' => isset( $movie['title'] ) ? sanitize_text_field( $movie['title'] ) : null,
        'playback_format' => isset( $movie['playback_format'] ) && in_array( $movie['playback_format'], $playback_formats ) ? $movie['playback_format'] : null,
        'description' => isset( $movie['description'] ) ? sanitize_text_field( $movie['description'] ) : null,
        );

}

endif;

// save movie data 
if ( ! empty( $clean ) ) { 
    update_post_meta( $post_id, 'repeatable_fields', $clean ); 
} else
    delete_post_meta( $post_id, 'repeatable_fields' ); 
}

I will say that I ran into the same issue that we talked about in the chat.... I about flipped when after all this I was still running into the second and third 'playback_format' radios weren't evening posting. But finally, I noticed that you are wrapping the radios in a <form> element. I'm convinced this is the problem because as soon as I got rid of it, it worked.

I would suggest you try dropping that extra <Form> from your code first to see if that solves it. It might be the rest of your code works. If not, then I'm pretty sure mine does now... however, I've changed some names and some IDs so that will probably effect the rest of the work that relies on this.

And finally, b/c of how I changed everything, I needed to modify your add-row JS. It is simplified here as I didn't want to deal with the tinyMCE editors, so I just left the textareas for my testing purposes.

Duplicate Row with Javascript

/*********************** Add Row Button Click *****************************/ 

$( '#add-row' ).click(function() { 
var rowCount = $('#repeatable-fieldset-one').find('.single-movie-row').not(':last-child').size(); 
var newRowCount = rowCount + 1;

var row = $( '.empty-row.screen-reader-text' ).clone(true); 

// Loop through all inputs
row.find('input, textarea, label').each(function(){ 

    if ( !! $(this).attr('id') )
        $(this).attr('id',  $(this).attr('id').replace('[%s]', '[' + newRowCount + ']') );  // Replace for

    if ( !! $(this).attr('name') )
        $(this).attr('name',  $(this).attr('name').replace('[%s]', '[' + newRowCount + ']') );  // Replace for

    if ( !! $(this).attr('for') )
        $(this).attr('for',  $(this).attr('for').replace('[%s]', '[' + newRowCount + ']') );  // Replace for

});

row.removeClass( 'empty-row screen-reader-text' ).find('.movie_rank_number').val('# '+newRowCount);
row.insertBefore( '.empty-row' ); 

// if row count hits 10, hide the add row button 
if ( newRowCount == 10 ) { 
jQuery('#add-row').fadeOut(); 
} 

return false; 
});
1
  • Oh man!!!!! If I could thumbs up this 50x I would. This just solved the MAJOR issue I was facing that was holding me back on a client project. I can not thank you enough!!!! YOU JUST MADE MY WEEKEND AND MY WEEK!!! THANK YOU SO MUCH!! SO MUCH!! I found the major issue was that extra form wrapper you mentioned. What a little bugger!
    – EHerman
    Feb 23, 2014 at 14:54

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.