WordPress custom meta fields, last input field gets “undefined index” but works after re-post

Im pretty new to php and have some issues with my code.
Ive been struggling with this for a few days now, researching, editing with no luck…
Please help a bro and explain how to solve my problem!!

Purpose with code

Read More

Adding custom meta fields to the dashboard post page so the user can add image(s) to a post.

ISSUES

The code works but is not 100% functional.
When adding images to the meta fields following happens

  • Adding ONLY one image, everything works.
  • Adding MORE than one image, the last image “src field” gets this
    error in the input field:

“…Undefined index: src in…functions.php line 123”

Line 123 is:

<td><input type="text" class="widefat" name="src[]" value="<?php if ($field['src'] != '') echo esc_attr( $field['src'] ); ?>" /></td>

and the array looks like this:

Array
(
    [0] => Array
        (
            [title] => image-name1.gif
            [alt] => image description 1
            [src] => http://my-host.com/art/wp-content/uploads/2014/04/test1.gif
        )

    [2] => Array
        (
            [title] => image-nam2.gif
            [alt] => image description 2
            [src] => http://my-host.com/art/wp-content/uploads/2014/04/test3.gif
        )

    [3] => Array
        (
            [title] => image-name3.gif
            [alt] => image description 3
        )

)
  • When adding the last image again and update the post everything
    works!

Research

I think it has to be something with the function that, in some way, checks the src input field if the field got any value because it only works when the input field excists.

It could also be something wrong in the last for loop that creates the array, before saving the meta fields.

function.php:

// add meta box to screen
function add_meta_boxes() {
    add_meta_box( 'image-meta-fields', 
                 'Add Image to Post', 
                 'image_meta_field_display', 
                 'post', 
                 'normal', 
                 'high');
}
add_action('admin_init', 'add_meta_boxes', 1);

// output the meta box content
function image_meta_field_display() {
    global $post;

    $image_meta_fields = get_post_meta($post->ID, 'image_meta_fields', true);

    wp_nonce_field( 'image_meta_field_nonce', 'image_meta_field_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('#image-meta-field-one tbody>tr:last');
                return false;
            });
            $('.remove-row').on('click', function() {
                $(this).closest('tr').next().remove();
                $(this).closest('tr').remove();

                return false;
            });

            // 
            $('.add-image').click(function() {

                var send_attachment_bkp = wp.media.editor.send.attachment;
                var button = $(this);

                wp.media.editor.send.attachment = function(props, attachment) {

                $(button).closest('tr').prev().children('td').eq(0).find('input').val(attachment.title); //set title
                $(button).closest('tr').prev().children('td').eq(1).find('input').val(attachment.alt); // set alt (description)
                $(button).closest('td').prev().children().val(attachment.url); // set url

                wp.media.editor.send.attachment = send_attachment_bkp;
                }
                wp.media.editor.open();
                return false;       
            });
        });
    </script>

    <table id="image-meta-field-one" width="100%">
        <thead>
            <tr>
                <th width="30%">Name</th>
                <th width="50%">Description</th>
                <th width="10%"></th>
            </tr>
        </thead>
    <tbody>
    <?php

    if ( $image_meta_fields ) :

    foreach ( $image_meta_fields as $field ) {
    ?>
    <tr>
        <td><input type="text" class="widefat" name="title[]" value="<?php if ($field['title'] != '') echo esc_attr( $field['title'] ); ?>" /></td>
        <td><input type="text" class="widefat" name="alt[]" value="<?php if ($field['alt'] != '') echo esc_attr( $field['alt'] ); ?>" /></td>
        <td><a class="button remove-row" href="#">DEL</a></td>
    </tr>
    <tr>
        <th width="30%"></th>
        <td><input type="text" class="widefat" name="src[]" value="<?php if ($field['src'] != '') echo esc_attr( $field['src'] ); ?>" /></td>
        <td><a class="button add-image" href="#" value="" >ADD IMAGE</a></td>
    </tr>
    <?php
    }
    else :
        // show a blank one
    ?>
        <tr>
            <td><input type="text" class="widefat" name="title[]" /></td>
            <td><input type="text" class="widefat" name="alt[]" /></td>
            <td><a class="button remove-row" href="#">DEL</a></td>
        </tr>
        <tr>
            <th width="30%"></th>
            <td><input type="text" class="widefat" name="src[]" value=""/></td>
            <td><a class="button add-image" href="#">ADD IMAGE</a></td>
        </tr>
    <?php endif; ?>

            <!-- empty hidden one for jQuery -->
        <tr class="empty-row screen-reader-text">
            <td><input type="text" class="widefat" name="title[]" /></td>
            <td><input type="text" class="widefat" name="alt[]" /></td>
            <td><a class="button remove-row" href="#">DEL</a></td>
        </tr>
        <tr class="empty-row screen-reader-text">
            <th width="30%"></th>
            <td><input type="text" class="widefat" name="src[]" value="" /></td>
            <td><a class="button add-image" href="#">ADD IMAGE</a></td>
        </tr>
    </tbody>
    </table>

    <p><a class="button add-row" href="#">Add More Images</a></p>
    <?php
}

add_action('save_post', 'image_meta_field_save');

function image_meta_field_save($post_id) {
    if ( !isset( $_POST['image_meta_field_nonce'] ) ||
        ! wp_verify_nonce( $_POST['image_meta_field_nonce'], 'image_meta_field_nonce' ) )
    return;

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
    return;

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

    $old = get_post_meta($post_id, 'image_meta_fields', true);
    $new = array();

    $titles = $_POST['title'];
    $alts = $_POST['alt'];
    $srcs = $_POST['src'];

    $count = count( $titles );

    for ( $i = 0; $i < $count; $i++ ) {
        if ( $titles[$i] != '' ) :
                $new[$i]['title'] = wp_filter_post_kses( $titles[$i] );

        if ( $alts[$i] != '' ) 
                $new[$i]['alt'] = wp_filter_post_kses ( $alts[$i] );

        if ( $srcs[$i] != '' ) 
                $new[$i]['src'] = wp_filter_post_kses( $srcs[$i] );
        endif;
    }

    if ( !empty( $new ) && $new != $old )
        update_post_meta( $post_id, 'image_meta_fields', $new );
    elseif ( empty($new) && $old )
        delete_post_meta( $post_id, 'image_meta_fields', $old );
}

Outputting the meta array in the front-end like this:

<?php 
    $get_images = get_post_meta( get_the_id(), 'image_meta_fields', true ); 

        if ( !empty( $get_images ) ) {
            foreach ( $get_images as $key => $image ) :
                    if ( $key === 0 )
                        echo "<img class='post-image' title='" . $image['title'] . "' alt='" . $image['alt'] . "' src='" . $image['src'] . "'/>";
                    else
                        echo "<img class='post-thn' title='" . $image['title'] . "' alt='" . $image['alt'] . "' src='" . get_image_thumbnail($image['src']) ."'/>";
            endforeach;
        } 
    ?>    

Related posts

Leave a Reply

1 comment

  1. Inspecting this variables:

    $titles = $_POST['title'];
    $alts = $_POST['alt'];
    $srcs = $_POST['src'];
    

    I saw that $titles and $alts were coming out of order:

    enter image description here

    The problem is when you clone this block:

    <!-- empty hidden one for jQuery -->
    <tr class="empty-row screen-reader-text">
        <td><input type="text" class="widefat" name="title[]" /></td>
        <td><input type="text" class="widefat" name="alt[]" /></td>
        <td><a class="button remove-row" href="#">DEL</a></td>
    </tr>
    <tr class="empty-row screen-reader-text">
        <th width="30%"></th>
        <td><input type="text" class="widefat" name="src[]" value="" /></td>
        <td><a class="button add-image" href="#">ADD IMAGE</a></td>
    </tr>
    

    It becomes this:

    <!-- empty hidden one for jQuery -->
    <tr class="empty-row screen-reader-text">
        <td><input type="text" class="widefat" name="title[]" /></td>
        <td><input type="text" class="widefat" name="alt[]" /></td>
        <td><a class="button remove-row" href="#">DEL</a></td>
    </tr>
        <tr class="">
            <td><input type="text" class="widefat" name="title[]" /></td>
            <td><input type="text" class="widefat" name="alt[]" /></td>
            <td><a class="button remove-row" href="#">DEL</a></td>
        </tr>
        <tr class="">
            <th width="30%"></th>
            <td><input type="text" class="widefat" name="src[]" value="" /></td>
            <td><a class="button add-image" href="#">ADD IMAGE</a></td>
        </tr>
    <tr class="empty-row screen-reader-text">
        <th width="30%"></th>
        <td><input type="text" class="widefat" name="src[]" value="" /></td>
        <td><a class="button add-image" href="#">ADD IMAGE</a></td>
    </tr>
    

    Instead of this:

        <tr class="">
            <td><input type="text" class="widefat" name="title[]" /></td>
            <td><input type="text" class="widefat" name="alt[]" /></td>
            <td><a class="button remove-row" href="#">DEL</a></td>
        </tr>
        <tr class="">
            <th width="30%"></th>
            <td><input type="text" class="widefat" name="src[]" value="" /></td>
            <td><a class="button add-image" href="#">ADD IMAGE</a></td>
        </tr>
    <!-- empty hidden one for jQuery -->
    <tr class="empty-row screen-reader-text">
        <td><input type="text" class="widefat" name="title[]" /></td>
        <td><input type="text" class="widefat" name="alt[]" /></td>
        <td><a class="button remove-row" href="#">DEL</a></td>
    </tr>
    <tr class="empty-row screen-reader-text">
        <th width="30%"></th>
        <td><input type="text" class="widefat" name="src[]" value="" /></td>
        <td><a class="button add-image" href="#">ADD IMAGE</a></td>
    </tr>
    

    It can be solved adding an ID to the first <tr>:

    <!-- empty hidden one for jQuery -->
    <tr id="clonable" class="empty-row screen-reader-text">
    

    And adjusting this in $('.add-row').on('click', ...);:

    row.removeClass('empty-row screen-reader-text');
    row.removeAttr('id');
    row.insertBefore('#clonable');