1

I have created a Custom Post Type to publish multiple images together.

--->>> title <<<---
--->>> image default - thumbnail <<<---
--->>> cotent <<<---
--->>>images<<<---
--->>>images<<<---
.....

The first three sections (title, image default and content) is the basics. Is ready.

I thought about using custom metabox and add each image URL. However, add url by url is nothing intuitive and a lot more work for the user, be it newbie or advanced. Furthermore, the amount will vary pictures can be 1, may be 5 may be 10 and so on....

So, how do I add multiple images within a single post CPT?

The Dashboard would look like this:

--->>> title <<<---
--->>> image default - thumbnail <<<---
--->>> cotent <<<---
--->>> add images<<<---

How can I do this? Be a plugin or direct in function.php.

  • 1
    Please post your answer in a separate post and mark it as accepted (green checkbox) to indicate this question is resolved. – Ethan Jinks O'Sullivan Aug 28 '16 at 3:22
1

As you rightly note storing URL is inconvenient for users. Also it is inconvenient for code, since it is hard to perform such operations as outputting different size of image when URL is all you have. It is rather challenging to do a generic reversal from image URL to its attachment data.

For storage typical practice is to store attachment IDs. They are friendly for storage, querying, output, and resilient to changes like image sizes being replaced.

Unfortunately natively WP only provides interface to store a single image — featured image aka post thumbnail.

For the structure you describe basic approaches would be:

  1. Store thumbnail as featured image and rest of images in content, using native or plugin-extended gallery functionality. This just works and is convenient/familiar to users, but leaves more space for them to break it.
  2. Implement a custom meta box with custom interface for images. Core is of little help in this, but there are multiple custom field frameworks around that can be helpful, it's obviously a common use case. This makes development more challenging, but interface more specialized and with less room for user error.
1

As I understand it, you will need create a meta box with fields and use jQuery to add/remove the image boxes. You can also use jQuery UI to make the fields draggable if you like.

Your meta box code will look something like this

// Fields
$prefix = 'your_prefix_';

$custom_meta_fields = array(
array(
   'label' => 'Gallery Images',
   'desc' => 'Add additional images for this portfolio item.',
   'id' => $prefix.'gallery_images',
   'scope' => array('your_custom_post_type'),
   'type' => 'repeatable_image',
),
);

// Add the Meta Box
function add_custom_meta_box()
{
    $post_types = array('your_custom_post_type', 'page', 'post');
    foreach ($post_types as $post_type) {
        add_meta_box(
            'custom_meta_box', // $id
            'Additional Information', // $title
            'show_custom_meta_box', // $callback
             $post_type,
            'normal', // $context
            'high' // $priority
        );
    }
}
add_action('add_meta_boxes', 'add_custom_meta_box');
// The Callback
function show_custom_meta_box()
{
    global $custom_meta_fields, $post;

// Use nonce for verification
echo '<input type="hidden" name="custom_meta_box_nonce" value="'.wp_create_nonce(basename(__FILE__)).'" />';
// Begin the field table and loop
echo '<table class="form-table">';

    foreach ($custom_meta_fields as $field) {
        //Check if scope matches post type
$scope = $field[ 'scope' ];
        $field_output = false;
        foreach ($scope as $scopeItem) {
            switch ($scopeItem) {
default: {
if ($post->post_type == $scopeItem) {
    $field_output = true;
}
break;
}
}
            if ($field_output) {
                break;
            }
        }
        if ($field_output) {
            // get value of this field if it exists for this post
$meta = get_post_meta($post->ID, $field['id'], true);
            $row = 0;
// begin a table row with
echo '<tr>
<th><label for="'.$field['id'].'">'.$field['label'].'</label></th>
<td>';
            switch ($field['type']) {
// text
case 'text':
echo '<input type="text" name="'.$field['id'].'" id="'.$field['id'].'" value="'.$meta.'" size="30" />
<br /><span class="description">'.$field['desc'].'</span>';
break;
// repeatable
case 'repeatable_image':
 echo '<a class="repeatable-add button" href="#">+</a>
         <ul id="'.$field['id'].'-repeatable" class="custom_repeatable">';
 $i = 0;
 if ($meta) {
     foreach ($meta as $row) {
         echo '<li><span class="sort hndle">|||</span>
                     <input type="text" class="img_field" name="'.$field['id'].'['.$i.']" id="'.$field['id'].'" value="'.$row.'" size="30" />
                     <a class="repeatable-remove button" href="#">-</a></li>';
         ++$i;
     }
 } else {
     echo '<li><span class="sort hndle">|||</span>
                 <input class="img_field" type="text" name="'.$field['id'].'['.$i.']" id="'.$field['id'].'" value="" size="30" />
                 <a class="repeatable-remove button" href="#">-</a></li>';
 }
 echo '</ul>
     <span class="description">'.$field['desc'].'</span>';
break;
} //end switch
echo '</td></tr>';
        }
    } // end foreach
echo '</table>'; // end table
}

// Save the Data
function save_custom_meta($post_id)
{
    global $custom_meta_fields;
// verify nonce
if (!isset($_POST['custom_meta_box_nonce']) || !wp_verify_nonce($_POST['custom_meta_box_nonce'], basename(__FILE__))) {
    return $post_id;
}
// check autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
    return $post_id;
}
// check permissions
if ('page' == $_POST['post_type']) {
    if (!current_user_can('edit_page', $post_id)) {
        return $post_id;
    } elseif (!current_user_can('edit_post', $post_id)) {
        return $post_id;
    }
}
// loop through fields and save the data
foreach ($custom_meta_fields as $field) {
    $old = get_post_meta($post_id, $field['id'], true);
    if (isset($_POST[$field['id']])) {
        $new = $_POST[$field['id']];
        if ($field['type'] === 'repeatable_ad' || $field['type'] === 'repeatable_image') {
            $new = array_values($new);
        }
    }
    if ($new && $new != $old) {
        update_post_meta($post_id, $field['id'], str_replace('"', "'", $new));
    } elseif ('' == $new && $old) {
        delete_post_meta($post_id, $field['id'], $old);
    }
} // end foreach
}
add_action('save_post', 'save_custom_meta');

and your js will need to be something like this

jQuery(document).on('click', '.img_field', function(e) {



var clicked_field = e.target.name;


var custom_uploader;


    e.preventDefault();

    //If the uploader object has already been created, reopen the dialog
    if (custom_uploader) {
        custom_uploader.open();
        return;
    }

    //Extend the wp.media object
    custom_uploader = wp.media.frames.file_frame = wp.media({
        title: 'Select Image',
        button: {
            text: 'Select Image'
        },
        multiple: false
    });

    //When a file is selected, grab the URL and set it as the text field's value
    custom_uploader.on('select', function() {
        attachment = custom_uploader.state().get('selection').first().toJSON();
        jQuery('input[name="'+ clicked_field +'"]').val(attachment.url);
        jQuery('.custom_preview_image').attr('src', attachment.url);
        jQuery('.custom_media_image').attr('src',attachment.url);
    });

    //Open the uploader dialog
    custom_uploader.open();

});


jQuery('.repeatable-add').click(function() {
    field = jQuery(this).closest('td').find('.custom_repeatable li:last').clone(true);
    fieldLocation = jQuery(this).closest('td').find('.custom_repeatable li:last');
    jQuery('input', field).val('').attr('name', function(index, name) {
        return name.replace(/(\d+)/, function(fullMatch, n) {
            return Number(n) + 1;
        });
    })
    field.insertAfter(fieldLocation, jQuery(this).closest('td'))
    return false;
});

jQuery('.repeatable-remove').click(function(){
    jQuery(this).parent().remove();
    return false;
});

jQuery('.custom_repeatable').sortable({
    opacity: 0.6,
    revert: true,
    cursor: 'move',
    handle: '.sort'
});

I haven't tested this, so let me know if it doesn't work but it should give you a good head start. Having said all that, it's much easier to use ACF or CMB2 to do this sort of thing. Anyway, I hope this helps.

0

Advanced custom field plugin provides this functionality through an additional add on named acf repeater field www.advancedcustomfields.com/resources/repeater in which you can add the same field multiple times in a post

0

UPDATED

I got it! I made this way, I created a gallery within the wordpress editor. And with foreach, I took the ID of each image and display in the Loop:

<?php
global $post;
$gallery = get_post_gallery_images( $post );

foreach( $gallery as $image_url ) {
?>
<div class="item">
  <figure style="margin: 0px !important" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
      <a href="<?php echo $image_url ?>" itemprop="contentUrl" data-size="1800x1200" data-index="0">
          <img src="<?php echo $image_url ?>" class="image" itemprop="thumbnail" alt="">
      </a>
  </figure>
</div>
<?php 
}
?>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.