Add the Meta Box

// Register the meta box
function wp_custom_meta_box() {
    add_meta_box(
        'custom_meta_box',          // ID
        'Custom Meta Box',          // Title
        'wp_custom_meta_box_html',  // Callback function
        'post',                     // Post type
        'normal',                   // Context
        'high'                      // Priority
    );
}
add_action('add_meta_boxes', 'wp_custom_meta_box');

function wp_custom_meta_box_html($post) {
    wp_nonce_field('save_custom_meta_box', 'custom_meta_box_nonce');
    
    // Get existing data if available
    $meta_data = get_post_meta($post->ID, '_custom_meta_box', true);
    
    echo '<div id="repeatable-fieldset-one">';
    
    if ($meta_data) {
        foreach ($meta_data as $index => $data) {
            wp_custom_repeatable_field($index, $data);
        }
    } else {
        wp_custom_repeatable_field(0);
    }
    
    echo '</div>';
    
    echo '<button id="add-row" class="button">Add Another</button>';
    
    // JS to handle adding/removing repeatable fields
    ?>
    <script>
        jQuery(document).ready(function($) {
            let i = <?php echo count($meta_data) ? count($meta_data) : 1; ?>;
            
            $('#add-row').on('click', function(e) {
                e.preventDefault();
                let row = <?php ob_start(); wp_custom_repeatable_field('INDEX'); echo json_encode(ob_get_clean()); ?>;
                $('#repeatable-fieldset-one').append(row.replace(/INDEX/g, i));
                i++;
            });
            
            $(document).on('click', '.remove-row', function(e) {
                e.preventDefault();
                $(this).closest('.repeatable-row').remove();
            });
        });
    </script>
    <?php
}

function wp_custom_repeatable_field($index = 0, $data = []) {
    $image = isset($data['image']) ? $data['image'] : '';
    $url = isset($data['url']) ? $data['url'] : '';
    $text = isset($data['text']) ? $data['text'] : '';
    
    ?>
    <div class="repeatable-row">
        <div>
            <label>Upload Image</label><br/>
            <input type="text" name="custom_meta_box[<?php echo $index; ?>][image]" id="image_url_<?php echo $index; ?>" value="<?php echo esc_attr($image); ?>" class="image-url-field">
            <button class="button upload-image-button">Upload</button>
        </div>
        <div>
            <label>Link URL</label><br/>
            <input type="text" name="custom_meta_box[<?php echo $index; ?>][url]" value="<?php echo esc_attr($url); ?>" class="widefat">
        </div>
        <div>
            <label>Link Text</label><br/>
            <input type="text" name="custom_meta_box[<?php echo $index; ?>][text]" value="<?php echo esc_attr($text); ?>" class="widefat">
        </div>
        <button class="remove-row button">Remove</button>
    </div>
    <?php
}

2. Save Meta Box Data

function wp_save_custom_meta_box($post_id) {
    if (!isset($_POST['custom_meta_box_nonce']) ||
        !wp_verify_nonce($_POST['custom_meta_box_nonce'], 'save_custom_meta_box')) {
        return $post_id;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return $post_id;
    }

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

    $meta_data = isset($_POST['custom_meta_box']) ? $_POST['custom_meta_box'] : [];

    update_post_meta($post_id, '_custom_meta_box', $meta_data);
}
add_action('save_post', 'wp_save_custom_meta_box');

3. Handle Image Upload (JavaScript)

jQuery(document).ready(function($) {
    var file_frame;
    
    $(document).on('click', '.upload-image-button', function(e) {
        e.preventDefault();
        
        var inputField = $(this).prev('.image-url-field');
        
        if (file_frame) {
            file_frame.open();
            return;
        }
        
        file_frame = wp.media.frames.file_frame = wp.media({
            title: 'Select an Image',
            button: {
                text: 'Use this Image'
            },
            multiple: false
        });
        
        file_frame.on('select', function() {
            var attachment = file_frame.state().get('selection').first().toJSON();
            inputField.val(attachment.url);
        });
        
        file_frame.open();
    });
});

 

Bài viết liên quan

post-no-image

Add the Meta Box Upload Multiple Images and multiple metabox

post-no-image

Kỹ thuật debounce trong javascript – Trì hoãn nhập từ khóa trong ô input

post-no-image

Thêm VS Code snippets

post-no-image

Query only seach by title

post-no-image

Hướng dẫn tạo form có validate, upload file nhiều bước và xử lý ajax

post-no-image

Chia sẻ một số thư viện loading css đẹp