Create Repeater Post Meta in WordPress
In previous post we learnt what is a post meta data. Today we are going to see what is a repeater post meta data. In other words it is post meta with array values.
Ok that wasn't a good definition, Lets say we want to add a post meta "Amenities" to our same custom post type "property". These amenities can vary per property so we can add different amenities for each property. When we save this amenities post meta it will be saved as an array in database. I have created the same meta box for this post specifically but technically it can be placed in the same post meta box we created in previous post.
We will create post-meta.php which utilizes wordpress' action hook "add_meta_boxes" for drawing our meta box and use "save_post_property" hook to save our repeater post meta. We will need javascript which will contain code to add more amenities when clicked the add row button and delete a specific amenity from existing amenities. I have also added some styles in style.css.
post-meta.php
<?php
add_action('add_meta_boxes', 'add_property_metas');
function add_property_metas(){
add_meta_box('property_meta_box', __('Post Data', 'text-domain'), 'draw_property_meta', ['property']);
}
function draw_property_meta($post){
wp_enqueue_style('post-meta', 'URL_OF_OUR_STYLESHEET');
wp_enqueue_script('post-meta', 'URL_OF_OUR_JAVASCRIPT');
$amenities = get_post_meta($post->ID, 'amenities', true);
?>
<div class="amenities">
<h3>
<?php _e('Amenities', 'text-domain');?>
</h3>
<table class="form-table">
<thead>
<tr>
<th class="w-auto"> </th>
<th class="delete"> </th>
</tr>
</thead>
<tbody>
<?php
// If there are amenities already saved show it
if(!empty($amenities)){
foreach($amenities as $i => $amenity){?>
<tr>
<td><input type="text" name="amenities[<?=$i;?>]" class="large-text" value="<?=$amenity?>" /></td>
<td><a href="javascript:void(0)" class="delete-amenity"><span class="dashicons dashicons-trash"></span></a></td>
</tr>
<?php
}
}?>
</tbody>
</table>
<button type="button" class="button add-more"><?php _e('+ Add More', 'text-domain' );?></button>
</div>
<?php
}
// Add action to save amenities only for property post type
add_action('save_post_property', 'save_property_meta');
function save_property_meta($post_id){
$amenities = [];
if(!empty($_POST['amenities'])){
$amenities = $_POST['amenities'];
$amenities = array_map('trim', $amenities);
$amenities = array_map('esc_html', $amenities);
$amenities = array_filter($amenities);
}
update_post_meta($post_id, 'amenities', $amenities);
}
javascript.js
<script>
jQuery(document).ready(function(){
const $ = jQuery;
// Add another row for amenity
$(".amenities .add-more").click(function(){
let last_row = $(".amenities > .form-table > tbody > tr");
let count = last_row.length === 0 ? 1 : last_row.length + 1;
let row = '<tr>'+
'<td><input type="text" name="amenities['+ count +']" class="large-text" value="" /></td>'+
'<td><a href="javascript:void(0)" class="delete-amenity"><span class="dashicons dashicons-trash"></span></a></td>'+
'</tr>';
$(".amenities > .form-table > tbody").append(row);
});
// Delete a specific amenity from list
$(".amenities > .form-table").on("click", ".delete-amenity", function(){
$(this).closest("tr").remove();
});
});
</script>
style.css
.amenities h3{
margin-top: 20px;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #ddd;
font-size: 14px;
}
.amenities .form-table{
margin-bottom: 20px !important;
}
.amenities .form-table,
.amenities .form-table th,
.amenities .form-table td{
border: 1px solid #ddd;
padding: 10px;
}
.amenities .form-table th.w-auto{
width: auto;
}
.amenities .form-table th.delete{
width: 20px;
}
.amenities a{
text-decoration: none;
}
.amenities a:focus{
box-shadow: none;
}
The file post-meta.php needs to be included in our functions.php to work.
include('post-meta.php');This is how our post meta box will look like: