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 );
}
?>
@markjaquith
Copy link

  • stripslashes() your $_POST values.
  • esc_attr() for anything that echoes in an HTML attribute. Especially on line 72!

@helen
Copy link
Author

helen commented Jan 11, 2012

Yes, sir.

@da1nonly
Copy link

thanks for the code i was wondering, how can i add another custom fields so that i can save video and audio in two different custom fields

@oterox
Copy link

oterox commented Jan 24, 2013

it would be possible to make the rows sortable?

@oterox
Copy link

oterox commented Jan 24, 2013

forget it, i've done it :)

@lonerunner
Copy link

How to use radio buttons instead of select box ?

@developez
Copy link

Where I need to put the code? in functions.php?

@deemi
Copy link

deemi commented Apr 18, 2014

Thanx alot its very helpful to me - just edit some code. :)

@reypm
Copy link

reypm commented Jun 6, 2014

@helenhousandi how I use this approach in a plugin? I tried as this post show and can't get it to work, could any of the experts here give me some help?

@majadc
Copy link

majadc commented Jun 25, 2014

Hi, it's so great. I have a question. Do you know to implement group of input type checkbox to this solution? I want to want to be able to save more than one value for the same field. I appreciate any help. Thank you.

@ranawarez
Copy link

I like the results I see in the single.php file

@levipadre
Copy link

Hi,
Could you help me how can I unserialize the result?
I made my own filed and this is the result:
a:1:{i:0;a:1:{s:7:"address";s:6:"Russia";}}

Thanks,

@haltaction
Copy link

Try to use function get_post_meta( $post_id, $key) http://codex.wordpress.org/Function_Reference/get_post_meta

@Ld9Gupta
Copy link

hey i am writing all of the above code but there is no any response means there is no any meta box is created

Copy link

ghost commented Jul 22, 2015

What should I use in my template in order to display a certain field value in my webpage?

@unfinishedCode
Copy link

Awesome! Thanks for sharing this. It is very similar to what I needed. One of my repeating inputs, is repeating.

@ZaheerAbbasAghani
Copy link

how can i add repeatable fields in my code

__('Music Albums'), 'singular_label' => __('Music Album'), 'public' => true, 'show_ui' => true, 'capability_type' => 'post', 'hierarchical' => true, 'has_archive' => true, 'supports' => array('title', '', 'thumbnail'), 'rewrite' => array('slug' => 'musicalbums', 'with_front' => false), ); //Register type and custom taxonomy for type. register_post_type( 'musicalbums' , $args ); register_taxonomy("business-type", array("musicalbums"), array("hierarchical" => true, "label" => "Album Types", "singular_label" => "Album Type", "rewrite" => true, "slug" => 'album-type')); add_action("admin_init", "music_albums_add_meta"); function music_albums_add_meta(){ add_meta_box("musicalbum-meta", "Tracks", "Music_albums_meta_options", "musicalbums", "normal", "high"); } function Music_albums_meta_options(){ global $post; if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id; $custom = get_post_custom($post->ID); $upload_track = $custom["upload_track"][0]; $tracktitle = $custom["tracktitle"][0]; $artist= $custom["artist"][0]; $imageurl = $custom["imageurl"][0]; $iconurl = $custom["iconurl"][0]; $downloadText = $custom["downloadText"][0]; $ExtraInfoText = $custom["ExtraInfoText"][0]; ?>
Upload MP3 :
ID ); //require_once('getid3/getid3.php'); $prefix = 'sample_'; $fields = array( array( // Text Input 'label' => 'Text Input', // 'desc' => 'A description for the field.', // description 'id' => $prefix.'text', // field id and name 'type' => 'text' // type of field ), array( // Textarea 'label' => 'Textarea', // 'desc' => 'A description for the field.', // description 'id' => $prefix.'textarea', // field id and name 'type' => 'textarea' // type of field ) ); ?>
Track Title :
Artist :
Icon Url:
Download Link :
Extra Info Text:

Add Another

add_action('save_post', 'business_manager_save_extras');
function business_manager_save_extras(){
global $post;
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ){
//if you remove this the sky will fall on your head.
return $post_id;
}else{
update_post_meta($post->ID, "iconurl",
$_POST["iconurl"]);
update_post_meta($post->ID, "tracktitle", $_POST["tracktitle"]);
update_post_meta($post->ID, "artist", $_POST["artist"]);
update_post_meta($post->ID, "upload_track",
$_POST["upload_track"]);
update_post_meta($post->ID, "upload_track_two",
$_POST["upload_track_two"]);
update_post_meta($post->ID, "imageurl", $_POST["imageurl"]);
update_post_meta($post->ID, "downloadText", $_POST["downloadText"]);
update_post_meta($post->ID, "ExtraInfoText", $_POST["ExtraInfoText"]);
}
}

add_filter("manage_edit-musicalbums_columns", "business_manager_edit_columns");
function business_manager_edit_columns($columns){
$columns = array(
"cb" => "<input type="checkbox" />",
"title" => "Albums Name",
"upload_track" => "Track",
"ExtraInfoText" => "Extra Info Text",
"cat" => "Category",
);
return $columns;
}

add_action("manage_musicalbums_posts_custom_column",
"business_manager_custom_columns");
function business_manager_custom_columns($column){
global $post;
$custom = get_post_custom();
switch ($column)
{

case "upload_track":
$upload_track= $custom["upload_track"][0].'
';
echo $upload_track;
break;
case "ExtraInfoText":
echo $custom["ExtraInfoText"][0];
break;
case "cat":
echo get_the_term_list($post->ID, 'business-type');
break;
}
}

}
?>

@mundothemes
Copy link

Display in single Post

PHP code

<?php $repeatable_fields = get_post_meta($post->ID, 'repeatable_fields', true);  if ( $repeatable_fields ) : ?>
    <div class="list">
        <?php foreach ( $repeatable_fields as $field ) { ?>
        <div class="row">
            <?php if($field['name'] != '') echo '<span class="field">'. esc_attr( $field['name'] ) . '</span>'; ?>
            <?php if($field['select'] != '') echo '<span class="field">'. esc_attr( $field['select'] ) . '</span>'; ?>
            <?php if($field['url'] != '') echo '<span class="field">'. esc_attr( $field['url'] ) . '</span>'; ?>
        </div>
        <?php } ?> 
    </div>
<?php endif; ?>

CSS Code

.list {
    float: left;
    width: 100%;
    padding: 15px;
}
.list .row {
    width: 100%;
    padding: 5px 0;
    float: left;
}
.list .row span.field {
    width: calc(100% / 3);
    float: left;
}

@FlibertyP
Copy link

hi @mundothemes, You could share your code, because the one you show is almost exactly what I want, ie a button to upload images, add a URL and just as the field is repeatable.

@biklik
Copy link

biklik commented Apr 1, 2017

Hi Helen! very nice work!
One question...is there any way to implement that code in a WordPress options page with settings API? I can build the form but the problema is to sanitize and save data.

Thank you!! +1

@moshebendavid
Copy link

Is there a way to create a dynamic ID to each name input when create based like

@sajadko
Copy link

sajadko commented Jul 9, 2017

it have problem :
Parse error: syntax error, unexpected '}' on line 119

@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