Skip to content

Instantly share code, notes, and snippets.

@helen
Created January 11, 2012 04:42
Show Gist options
  • Save helen/1593065 to your computer and use it in GitHub Desktop.
Save helen/1593065 to your computer and use it in GitHub Desktop.
Repeating Custom Fields in a Metabox
<?
/**
* Repeatable Custom Fields in a Metabox
* Author: Helen Hou-Sandi
*
* From a bespoke system, so currently not modular - will fix soon
* Note that this particular metadata is saved as one multidimensional array (serialized)
*/
function hhs_get_sample_options() {
$options = array (
'Option 1' => 'option1',
'Option 2' => 'option2',
'Option 3' => 'option3',
'Option 4' => 'option4',
);
return $options;
}
add_action('admin_init', 'hhs_add_meta_boxes', 1);
function hhs_add_meta_boxes() {
add_meta_box( 'repeatable-fields', 'Repeatable Fields', 'hhs_repeatable_meta_box_display', 'post', 'normal', 'default');
}
function hhs_repeatable_meta_box_display() {
global $post;
$repeatable_fields = get_post_meta($post->ID, 'repeatable_fields', true);
$options = hhs_get_sample_options();
wp_nonce_field( 'hhs_repeatable_meta_box_nonce', 'hhs_repeatable_meta_box_nonce' );
?>
<script type="text/javascript">
jQuery(document).ready(function( $ ){
$( '#add-row' ).on('click', function() {
var row = $( '.empty-row.screen-reader-text' ).clone(true);
row.removeClass( 'empty-row screen-reader-text' );
row.insertBefore( '#repeatable-fieldset-one tbody>tr:last' );
return false;
});
$( '.remove-row' ).on('click', function() {
$(this).parents('tr').remove();
return false;
});
});
</script>
<table id="repeatable-fieldset-one" width="100%">
<thead>
<tr>
<th width="40%">Name</th>
<th width="12%">Select</th>
<th width="40%">URL</th>
<th width="8%"></th>
</tr>
</thead>
<tbody>
<?php
if ( $repeatable_fields ) :
foreach ( $repeatable_fields as $field ) {
?>
<tr>
<td><input type="text" class="widefat" name="name[]" value="<?php if($field['name'] != '') echo esc_attr( $field['name'] ); ?>" /></td>
<td>
<select name="select[]">
<?php foreach ( $options as $label => $value ) : ?>
<option value="<?php echo $value; ?>"<?php selected( $field['select'], $value ); ?>><?php echo $label; ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="text" class="widefat" name="url[]" value="<?php if ($field['url'] != '') echo esc_attr( $field['url'] ); else echo 'http://'; ?>" /></td>
<td><a class="button remove-row" href="#">Remove</a></td>
</tr>
<?php
}
else :
// show a blank one
?>
<tr>
<td><input type="text" class="widefat" name="name[]" /></td>
<td>
<select name="select[]">
<?php foreach ( $options as $label => $value ) : ?>
<option value="<?php echo $value; ?>"><?php echo $label; ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="text" class="widefat" name="url[]" value="http://" /></td>
<td><a class="button remove-row" href="#">Remove</a></td>
</tr>
<?php endif; ?>
<!-- empty hidden one for jQuery -->
<tr class="empty-row screen-reader-text">
<td><input type="text" class="widefat" name="name[]" /></td>
<td>
<select name="select[]">
<?php foreach ( $options as $label => $value ) : ?>
<option value="<?php echo $value; ?>"><?php echo $label; ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="text" class="widefat" name="url[]" value="http://" /></td>
<td><a class="button remove-row" href="#">Remove</a></td>
</tr>
</tbody>
</table>
<p><a id="add-row" class="button" href="#">Add another</a></p>
<?php
}
add_action('save_post', 'hhs_repeatable_meta_box_save');
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;
$old = get_post_meta($post_id, 'repeatable_fields', true);
$new = array();
$options = hhs_get_sample_options();
$names = $_POST['name'];
$selects = $_POST['select'];
$urls = $_POST['url'];
$count = count( $names );
for ( $i = 0; $i < $count; $i++ ) {
if ( $names[$i] != '' ) :
$new[$i]['name'] = stripslashes( strip_tags( $names[$i] ) );
if ( in_array( $selects[$i], $options ) )
$new[$i]['select'] = $selects[$i];
else
$new[$i]['select'] = '';
if ( $urls[$i] == 'http://' )
$new[$i]['url'] = '';
else
$new[$i]['url'] = stripslashes( $urls[$i] ); // and however you want to sanitize
endif;
}
if ( !empty( $new ) && $new != $old )
update_post_meta( $post_id, 'repeatable_fields', $new );
elseif ( empty($new) && $old )
delete_post_meta( $post_id, 'repeatable_fields', $old );
}
?>
@sagar290
Copy link

@MajesticSJ this problem occurs because of the <? instead of <?php on the first line.

@thinzarwin
Copy link

thinzarwin commented Aug 2, 2018

i want to display repeatable field to specific page template.
I add the following code then repetable-fields was not display.

global $post;
if(!empty($post)) {
$pageTemplate = get_post_meta($post->ID, '_wp_page_template', true);
if($pageTemplate == 'inner_template.php' ){
add_meta_box( 'repeatable-fields', 'repeatable-fields', 'hhs_repeatable_meta_box_display', 'page', 'normal', 'high');
}
}
Please help me.

@rilwis
Copy link

rilwis commented Sep 26, 2018

I'd suggest using Meta Box Group for repeatable group of custom fields. It supports unlimited level nesting and supports all field types.

@HuuDuc97
Copy link

HuuDuc97 commented Aug 5, 2019

How to export data? Thank !!!

@Bipin13
Copy link

Bipin13 commented Sep 12, 2019

Thank For the code,
I face problem while removing column, if i remove from buttom it works fine, but when i remove random any field it remove its sibling fields too. and always select its very first value..

@BuddyHoli
Copy link

Dear Helen,
first of all, thank you so much. It seems that this is nearly what I need.
Sadly, I am not a really professional coder, just starting with wordpress metaboxes and custom fields. Actually, my proud success was to add custom fields to select or multiselect custom taxonomy terms and save them. Well, everybody´s starting low, right.

I have a big question. Would you be so kind to gibt me an example of one repeatable group of fields including 4 fields (like I created them)?

In the meantime, I will try this on my own, but many passages in your code look (actually) hard to understand for me :)

Let me know, if you like to help.
Best wishes and thanks again
Buddy

@ibneAbdulHaque
Copy link

ibneAbdulHaque commented Dec 14, 2020

Hi Dear Helen, glad to know that, your code is working fine. but when I try to upload media as a repeatable field then I am facing a few problems to solve this. please check my following code and let me know the solutions. if I get a solution then it will save lots of time. Already I wasted my 2 nights.

add_action('admin_init', 'custom_repeater_add_meta_boxes', 1);
function custom_repeater_add_meta_boxes() {
add_meta_box( 'custom_repeater_field', 'Custom Repeater Fields', 'custom_repeater_meta_box_display', array('lawncare_service'), 'normal', 'default');
}

function custom_repeater_meta_box_display()
{
global $post;

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

wp_nonce_field( 'custom_repeater_meta_box_nonce', 'custom_repeater_meta_box_nonce' );

?>

<script type="text/javascript">
jQuery(document).ready(function( $ ){
    $( '#add-row-custom' ).on('click', function() {
        var row = $( '.custom-empty-row.screen-reader-text' ).clone(true);
        row.removeClass( 'custom-empty-row screen-reader-text' );
        row.insertBefore( '#custom-repeatable-fieldset-one tbody>tr:last' );
        return false;
    });

    $( '.remove-row-custom' ).on('click', function() {
        $(this).parents('tr').remove();
        return false;
    });
});
</script>

<table id="custom-repeatable-fieldset-one" width="100%">
<thead>
    <tr>
        <th width="30%">Name</th>
        <th width="20%">Designation</th>
        <th width="20%">Image Upload</th>
        <th width="20%">URL</th>
        <th width="10%"></th>
    </tr>
</thead>
<tbody>
<?php

if ( $custom_repeater_field ) :

foreach ( $custom_repeater_field as $field ) {
?>
<tr>
    <td><input type="text" class="widefat" name="custom_name[]" value="<?php if($field['custom_name'] != '') echo esc_attr( $field['custom_name'] ); ?>" /></td>

    <td><input type="text" class="widefat" name="custom_designation[]" value="<?php if ($field['custom_designation'] != '') echo esc_attr( $field['custom_designation'] ); ?>" /></td>

    <td>
    	<?php	
    	$html = '<p class="description">Upload your Image here.</p>';
		$html .= '<input id="wp_custom_attachment" name="wp_custom_attachment" size="25" type="file" value="" />';

		// $filearray = get_post_meta( get_the_ID(), 'wp_custom_attachment', true );
		$this_file = $field['wp_custom_attachment']['url'];
		
		if ( $this_file == '' ) { 
		     $html .= '<div><p>Current file: ' . $this_file . '</p></div>'; 
		}
		echo $html; ?>
    </td>

    
    <td><input type="text" class="widefat" name="custom_url[]" value="<?php if ($field['custom_url'] != '') echo esc_attr( $field['custom_url'] ); ?>" /></td>

    <td><a class="button remove-row-custom" href="#">Remove</a></td>
</tr>
<?php
}
else :
// show a blank one
?>
<tr>
    <td><input type="text" class="widefat" name="custom_name[]" /></td>

    <td><input type="text" class="widefat" name="custom_designation[]" value="" /></td>

    <td>
    	<p class="description">Upload your Image here.</p>
		<input id="wp_custom_attachment_two" name="wp_custom_attachment" size="25" type="file" value="" />
    </td>


    <td><input type="text" class="widefat" name="custom_url[]" value="" /></td>

    <td><a class="button remove-row-custom" href="#">Remove</a></td>
</tr>
<?php endif; ?>

<!-- empty hidden one for jQuery -->
<tr class="custom-empty-row screen-reader-text">
    <td><input type="text" class="widefat" name="custom_name[]" /></td>

    
    <td><input type="text" class="widefat" name="custom_designation[]" value="" /></td>

    <td>
    	<p class="description">Upload your Image here.</p>
		<input id="wp_custom_attachment_three" name="wp_custom_attachment" size="25" type="file" value="" />
    </td>

    <td><input type="text" class="widefat" name="custom_url[]" value="" /></td>
      
    <td><a class="button remove-row-custom" href="#">Remove</a></td>
</tr>
</tbody>
</table>

<p><a id="add-row-custom" class="button" href="#">Add another</a></p>
<?php

}

add_action('save_post', 'custom_repeater_meta_box_save');
function custom_repeater_meta_box_save($post_id) {
if ( ! isset( $_POST['custom_repeater_meta_box_nonce'] ) ||
! wp_verify_nonce( $_POST['custom_repeater_meta_box_nonce'], 'custom_repeater_meta_box_nonce' ) )
return;

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

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

$old_custom = get_post_meta($post_id, 'custom_repeater_field', true);
$new_custom = array();

$names_custom = $_POST['custom_name'];
$custom_designation = $_POST['custom_designation'];
$custom_file = $_FILES['wp_custom_attachment'];
$custom_url = $_POST['custom_url'];

$count_custom = count( $names_custom );

for ( $j = 0; $j < $count_custom; $j++ ) {
    if ( $names_custom[$j] != '' ) :
        $new_custom[$j]['custom_name'] = stripslashes( strip_tags( $names_custom[$j] ) );

        if ( $custom_designation[$j] == '' )
            $new_custom[$j]['custom_designation'] = '';
        else
            $new_custom[$j]['custom_designation'] = stripslashes( $custom_designation[$j] );        
        if ( $custom_url[$j] == '' )
            $new_custom[$j]['custom_url'] = '';
        else
            $new_custom[$j]['custom_url'] = stripslashes( $custom_url[$j] ); 

        if ( ! empty( $custom_file[$j]['name'] ) ) {

		$upload_custom[$j] = wp_upload_bits($custom_file[$j]['name'], null, file_get_contents($custom_file[$j]['tmp_name']));
		if ( isset( $upload_custom[$j]['error'] ) && $upload_custom[$j]['error'] != 0 ) {
			wp_die( 'There was an error uploading your file. The error is: ' . $upload_custom[$j]['error'] );
		} else {
			// add_post_meta( $post_id, 'wp_custom_attachment', $upload_custom[$j] );
			// update_post_meta( $post_id, 'wp_custom_attachment', $upload_custom[$j] );
			$new_custom[$j]['wp_custom_attachment'] = $upload_custom[$j] ;
		}
	}

             // and however you want to sanitize
    endif;
}

if ( !empty( $new_custom ) && $new_custom != $old_custom )
    update_post_meta( $post_id, 'custom_repeater_field', $new_custom );
elseif ( empty($new_custom) && $old_custom )
    delete_post_meta( $post_id, 'custom_repeater_field', $old_custom );

}

/**

  • Add functionality for file upload.
    */
    function update_edit_form_custom() {
    echo ' enctype="multipart/form-data"';
    }
    add_action( 'post_edit_form_tag', 'update_edit_form_custom' );

?>

I don't know, what is the problem. please help me and save my time

@anjanavk1
Copy link

anjanavk1 commented Dec 18, 2021

thanks for this code ,it's working fine can anyone help me to implement this with checkbox ,when I tried it's not saving checkbox entries

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment