Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@markjaquith markjaquith commented Jan 11, 2012

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

This comment has been minimized.

Copy link
Owner Author

@helen helen commented Jan 11, 2012

Yes, sir.

@da1nonly

This comment has been minimized.

Copy link

@da1nonly da1nonly commented Mar 17, 2012

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

This comment has been minimized.

Copy link

@oterox oterox commented Jan 24, 2013

it would be possible to make the rows sortable?

@oterox

This comment has been minimized.

Copy link

@oterox oterox commented Jan 24, 2013

forget it, i've done it :)

@lonerunner

This comment has been minimized.

Copy link

@lonerunner lonerunner commented Feb 8, 2013

How to use radio buttons instead of select box ?

@developez

This comment has been minimized.

Copy link

@developez developez commented Mar 13, 2013

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

@deemi

This comment has been minimized.

Copy link

@deemi deemi commented Apr 18, 2014

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

@reypm

This comment has been minimized.

Copy link

@reypm 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

This comment has been minimized.

Copy link

@majadc 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

This comment has been minimized.

Copy link

@ranawarez ranawarez commented Jul 5, 2014

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

@levipadre

This comment has been minimized.

Copy link

@levipadre levipadre commented Nov 21, 2014

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

This comment has been minimized.

Copy link

@haltaction haltaction commented Dec 26, 2014

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

@Ld9Gupta

This comment has been minimized.

Copy link

@Ld9Gupta Ld9Gupta commented Jul 17, 2015

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

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Jul 22, 2015

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

@unfinishedCode

This comment has been minimized.

Copy link

@unfinishedCode unfinishedCode commented Aug 26, 2015

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

@ZaheerAbbasAghani

This comment has been minimized.

Copy link

@ZaheerAbbasAghani ZaheerAbbasAghani commented Jan 9, 2016

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

This comment has been minimized.

Copy link

@mundothemes mundothemes commented Apr 15, 2016

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

This comment has been minimized.

Copy link

@FlibertyP FlibertyP commented Feb 15, 2017

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

This comment has been minimized.

Copy link

@biklik 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

This comment has been minimized.

Copy link

@moshebendavid moshebendavid commented Apr 17, 2017

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

@sajadko

This comment has been minimized.

Copy link

@sajadko sajadko commented Jul 9, 2017

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

@sagar290

This comment has been minimized.

Copy link

@sagar290 sagar290 commented Sep 21, 2017

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

@thinzarwin

This comment has been minimized.

Copy link

@thinzarwin 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

This comment has been minimized.

Copy link

@rilwis 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

This comment has been minimized.

Copy link

@HuuDuc97 HuuDuc97 commented Aug 5, 2019

How to export data? Thank !!!

@Bipin13

This comment has been minimized.

Copy link

@Bipin13 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

This comment has been minimized.

Copy link

@BuddyHoli BuddyHoli commented Mar 6, 2020

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

This comment has been minimized.

Copy link

@ibneAbdulHaque 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

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