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. It is just a post meta data that contains an array of multiple values.

Create Repeater Post Meta Data in Wordpress
Ok that wasn't a good definition, Lets say we want to add a post meta "More Links" to our same custom post type "movie". These more links are external video source url which can vary we can add just two links or three or any number of links. When we store this more links 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.

So we will create post-meta.php which will be calling wordpress action hook for "add_meta_boxes" to draw our meta box and use "save_post" hook to save our repeater post meta. We will need javascript to which will contain code to add more links when clicked the add row button and delete a specific link from existing links. I have also added some styles in style.css.

post-meta.php

add_action( "add_meta_boxes", "add_movie_metas" );
function add_movie_metas(){
    add_meta_box( "movie_meta_box", "Meta Data", "draw_movie_meta", ["movie"] );
}

function draw_movie_meta($post){
    $more_links     = get_post_meta($post->ID, "more_links", true);
    wp_enqueue_style('post-meta', 'URL_OF_OUR_STYLESHEET');
    wp_enqueue_script('post-meta', 'URL_OF_OUR_JAVASCRIPT');
?>
    <div class="more-links">
        <h3><span class="dashicons dashicons-external"></span> <?php _e( "More Links", "textdomain" );?></h3>
        <table class="form-table">
            <thead>
                <tr>
                    <th><?php _e( "Title", "textdomain" );?></th>
                    <th><?php _e( "Link", "textdomain" );?></th>
                    <th class="delete"><span class="dashicons dashicons-trash"></span></th>
                </tr>
            </thead>
            <tbody>
                <?php
                //---- If there are links already saved show it. ----//
                if( !empty($more_links) ){
                    $i = 1;
                    foreach($more_links as $more_link){?>
                        <tr>
                            <td><input type="text" name="more_links[<?=$i;?>][title]" class="regular-text" value="<?=$more_link['title']?>" /></td>
                            <td><input type="text" name="more_links[<?=$i;?>][link]" class="regular-text" value="<?=$more_link['link']?>" /></td>
                            <td><a href="javascript:void(0)" class="delete-more-link"><span class="dashicons dashicons-trash"></span></a></td>
                        </tr>
                <?php
                    $i++;
                    }
                }?>
            </tbody>
        </table>
        <button type="button" class="button add-more-link-row"><?php _e( "Add Row", "textdomain" );?></button>
    </div>
<?php
}

add_action( "save_post", "save_movie_episode_meta" );
function save_movie_episode_meta($post_id){
    // --- Save post meta for movie & episode post types only --- //
    if( isset( $_POST["post_type"] ) && $_POST["post_type"] == "movie" ){

        // --- Save more links meta --- //
        $more_links = [];
        if(isset($_POST["more_links"])){
            foreach($_POST["more_links"] as $link_data){

                // --- Trim white spaces from title & link --- //
                $title = trim( $link_data["title"] );
                $link = trim( $link_data["link"] );

                // --- Add only links which are not empty --- //
                if(!empty($title) && !empty($link)){
                    $more_link = [];
                    $more_link["title"] = esc_html( $link_data["title"] );
                    $more_link["link"] = esc_url( $link_data["link"] );

                    $more_links[] = $more_link;
                }
            }
        }

        update_post_meta( $post_id, "more_links", $more_links );
    }
}?>

javascript.js

jQuery(document).ready(function(){
    var $ = jQuery;
    // ---- Add another row for link ----//
    $(".add-more-link-row").click(function(){
        var last_row = $(".more-links > .form-table > tbody > tr");
        var count = last_row.length == 0 ? 1 : last_row.length + 1;

        var row = '<tr>'+
                  '<td><input type="text" name="more_links['+ count +'][title]" class="regular-text" value="" /></td>'+
                  '<td><input type="text" name="more_links['+ count +'][link]" class="regular-text" value="" /></td>'+
                  '<td><a href="javascript:void(0)" class="delete-more-link"><span class="dashicons dashicons-trash"></span></a></td>'+
                  '</tr>';
        $(".more-links > .form-table > tbody").append(row);
    });

    //---- Delete a specific link from list ----//
    $(".more-links > .form-table").on("click", ".delete-more-link", function(){
        $(this).closest("tr").remove();
    });
});

style.css

.more-links h3{
    margin-top: 20px;
    margin-bottom: 10px;
    padding-bottom: 5px;
    border-bottom: 1px solid #ddd;
    font-size: 14px;
}
.more-links .form-table{
    margin-bottom: 20px !important;
}
.more-links .form-table,
.more-links .form-table th,
.more-links .form-table td{
    border: 1px solid #ddd;
    padding: 10px;
}
.more-links .form-table th.delete{
    width: 20px;
}
.more-links a{
    text-decoration: none;
}
.more-links a:focus{
    box-shadow: none;
}
Now we will include this post-meta.php in our functions.php to work.
include("post-meta.php");
This is how our post meta box will look like:

Repeater Post Meta Data in Wordpress