For the past one week, I have been trying to write custom PHP and jQuery code to upload additional variation images to each variation. I wrote some code, but it didn't work properly.
With the code below, I am:
How can I fix it?
function custom_allow_multiple_variation_images($loop, $variation_data, $variation) {
$variation_id = $variation->ID;
$field_name = 'variation_image';
$attachment_ids = get_post_meta($variation_id, $field_name, true);
echo '<div class="upload_variation_image">';
echo '<h4>Fotoğraf</h4>';
echo '<input type="hidden" name="' . $field_name . '[' . $loop . ']" id="' . $field_name . '" class="upload_image" value="' . esc_attr(implode(',', (array) $attachment_ids)) . '" />';
echo '<button class="upload_image_button button">Upload Image</button>';
if ($attachment_ids) {
$attachment_ids = explode(',', $attachment_ids);
foreach ($attachment_ids as $attachment_id) {
$image_src = wp_get_attachment_image_src($attachment_id, 'thumbnail');
if ($image_src) {
echo '<div class="uploaded_image_wrapper">';
echo '<img src="' . esc_url($image_src[0]) . '" alt="Uploaded Image" />';
echo '<span class="delete_variation_image" data-attachment_id="' . $attachment_id . '">Sil</span>';
echo '</div>';
}
}
}
echo '</div>';
}
add_action('woocommerce_variation_options', 'custom_allow_multiple_variation_images', 10, 3);
function custom_save_variation_image($variation_id) {
$field_name = 'variation_image';
$attachment_ids = isset($_POST[$field_name]) ? array_map('absint', explode(',', wc_clean($_POST[ $field_name][0]))) : array();
update_post_meta($variation_id, $field_name, implode(',', $attachment_ids));
}
add_action('woocommerce_save_product_variation', 'custom_save_variation_image', 10, 1);
add_action('woocommerce_save_product_variation', 'custom_save_variation_image', 10, 1);
function custom_variation_image_scripts() {
?>
<script>
jQuery(document).ready(function($) {
function custom_add_variation_image($wrapper) {
var file_frame;
if (file_frame) {
file_frame.open();
return;
}
file_frame = wp.media.frames.file_frame = wp.media({
title: 'Variation Image Select',
button: {
text: 'Add Additional Image'
},
multiple: true
});
file_frame.on('select', function() {
var attachment_ids = [];
var selection = file_frame.state().get('selection');
selection.map(function(attachment) {
attachment = attachment.toJSON();
attachment_ids.push(attachment.id);
});
$wrapper.find('.upload_image').val(attachment_ids.join(','));
$wrapper.find('.uploaded_image_wrapper').remove();
attachment_ids.forEach(function(attachment_id) {
var image_url = wp.media.attachment(attachment_id).get('url');
$wrapper.append('<div class="uploaded_image_wrapper"><img src="' + image_url + '" alt="Uploaded Image" /><span class="delete_variation_image" data-attachment_id="' + attachment_id + '">Sil</span></div>');
});
$wrapper.find('.upload_image').trigger('change'); // Trigger change event when selecting a variation image
});
file_frame.open();
}
function custom_remove_variation_image($wrapper, attachment_id) {
var attachment_ids = $wrapper.find('.upload_image').val().split(',');
var index = attachment_ids.indexOf(attachment_id);
if (index !== -1) {
attachment_ids.splice(index, 1);
$wrapper.find('.upload_image').val(attachment_ids.join(','));
}
$wrapper.find('.uploaded_image_wrapper[data-attachment_id="' + attachment_id + '"]').remove();
$wrapper.find('.upload_image').trigger('change'); // Trigger change event when removing a variation image
}
function custom_detect_variation_changes() {
$('form.cart').on('change', '.variation-control', function() {
$('.single_add_to_cart_button').prop('disabled', false);
});
// When a variation image changes, enable "Add to Cart" button
$('form.cart').on('change', '.upload_image', function() {
$('.single_add_to_cart_button').prop('disabled', false);
});
}
// Medya yükleyici olaylarını ekle
$(document).on('click', '.upload_image_button', function(e) {
e.preventDefault();
custom_add_variation_image($(this).closest('.upload_variation_image'));
});
$(document).on('click', '.delete_variation_image', function() {
var attachment_id = $(this).data('attachment_id');
custom_remove_variation_image($(this).closest('.upload_variation_image'), attachment_id);
});
custom_detect_variation_changes();
});
</script>
<style>
.uploaded_image_wrapper {
margin: 10px 0;
display: inline-block;
}
.uploaded_image_wrapper img {
max-width: 100px;
height: auto;
margin-right: 10px;
}
.delete_variation_image {
cursor: pointer;
color: #a00;
}
</style>
<?php
}
add_action( 'admin_footer', 'custom_variation_image_scripts' );
After LoicTheAztec's answer, I can add variation images from the variation tab, but nothing displays on the product page. You can see the image below about my latest situation.
As I have already implemented something similar in a custom plugin… Find below a simplified and compact working code version.
PHP
add_action( 'woocommerce_variation_options', 'add_additional_variation_image_ids', 10, 3 );
function add_additional_variation_image_ids( $loop, $variation_data, $variation ) {
$variation_id = $variation->ID;
$variation_object = wc_get_product($variation_id);
$attachment_ids = $variation_object->get_meta( 'additional_img_ids' ); // Get Attachments Ids
$attachment_ids = empty($attachment_ids) ? array( 0 => '' ) : $attachment_ids;
$count = count($attachment_ids);
$placeholder = esc_url( wc_placeholder_img_src() );
$upload_img_txt = esc_html__( 'Upload an image', 'woocommerce' );
$remove_img_txt = esc_html__( 'Remove this image', 'woocommerce' );
$add_txt = esc_html__( 'Add', 'woocommerce' );
$remove_txt = esc_html__( 'Remove', 'woocommerce' );
echo '<div class="custom-uploads">
<h4>' . __( 'Additional images:', 'woocommerce' ) . '</h4>';
// Loop through each existing attachment image ID
foreach( $attachment_ids as $index => $image_id ) {
// Add an Image field
printf('<div class="image-box"><p class="form-row form-row-wide upload_image">
<a href="#" class="upload_image_button tips %s" data-tip="%s" rel="%s"><img src="%s" />
<input type="hidden" name="additional_img_ids-%s-%s" class="upload_image_id" value="%s" /></a>
<p></div>',
$image_id ? 'remove' : '', $image_id ? $remove_img_txt : $upload_img_txt, $variation_id,
$image_id ? esc_url( wp_get_attachment_thumb_url( $image_id ) ) : $placeholder, $loop, $index, $image_id
);
}
// Add the buttons
printf('<div class="buttons-box"><p>
<button type="button" class="add-slot" data-loop="%d">%s</button>
<button type="button" class="remove-slot" data-loop="%d"%s>%s</button>
<input type="hidden" name="slot-index-%d" value="%s" /><input type="hidden" name="ph-img-%s" value="%s" /></a><p></div>',
$loop, $add_txt, $loop, $count == 1 ? ' style="display:none;"' : '', $remove_txt, $loop, $count, $loop, $placeholder
);
echo '</div>';
}
add_action( 'woocommerce_admin_process_variation_object', 'save_additional_variation_image_ids', 10, 2 );
function save_additional_variation_image_ids( $variation, $i )
{
if ( isset($_POST["slot-index-{$i}"]) && $_POST["slot-index-{$i}"] > 1 ) {
$attachment_ids = array(); // Initialize
// Loop through each posted attachment Id for the current variation
for( $k = 0; $k < $_POST["slot-index-{$i}"]; $k++ ) {
if ( isset($_POST["additional_img_ids-{$i}-{$k}"]) && ! empty($_POST["additional_img_ids-{$i}-{$k}"]) ) {
$attachment_ids[$k] =esc_attr($_POST["additional_img_ids-{$i}-{$k}"]); // Set it in the array
}
}
if( ! empty($attachment_ids) ) {
$variation->update_meta_data( 'additional_img_ids', $attachment_ids ); // save
}
}
}
JavaScript / jQuery (embedded in a function, can be enqueued from an external file):
add_action( 'admin_footer', 'additional_variation_image_js' );
function additional_variation_image_js() {
global $pagenow, $typenow;
if( in_array( $pagenow, array('post.php', 'post-new.php') ) && $typenow === 'product' ) :
?>
<script>
jQuery(function($) {
$('body').on('click', '.add-slot', function(){
const parent = $(this).closest('.custom-uploads'),
loop = $(this).data('loop');
var lastImageBox = $(parent).find('.image-box:last'),
hiddenInput = $('[name=slot-index-'+loop+']'),
index = parseInt(hiddenInput.val()),
inputNameUpd = 'additional_img_ids-'+loop+'-'+index,
inputPropsUpd = {'name': inputNameUpd, 'value': null}
placeholder = $('[name=ph-img-'+loop+']').val(),
clonedImgBox = lastImageBox.clone().insertBefore('.buttons-box'); // Insert a clone
clonedImgBox.find('.upload_image_id').prop(inputPropsUpd);
clonedImgBox.find('img').prop('src', placeholder);
clonedImgBox.find('a').removeClass('remove');
hiddenInput.val(index+1);
if ( index == 1 ) {
$(this).parent().find('.remove-slot').show();
}
}).on('click', '.remove-slot', function(){
const parent = $(this).closest('.custom-uploads'),
loop = $(this).data('loop');
var lastImageBox = $(parent).find('.image-box:last'),
hiddenInput = $('[name=slot-index-'+loop+']'),
index = parseInt(hiddenInput.val());
lastImageBox.remove();
hiddenInput.val(index-1);
if ( index == 2 ) {
$(this).hide();
}
});
});
</script>
<?php
endif;
}
CSS (embedded in a function, can be enqueued from an external file):
add_action( 'admin_head', 'additional_variation_image_css', 9999 );
function additional_variation_image_css() {
global $pagenow, $typenow;
if( in_array( $pagenow, array('post.php', 'post-new.php') ) && $typenow === 'product' ) :
?>
<style>
.custom-uploads .image-box,
.custom-uploads .buttons-box {
float:left;
width:76px;
}
.custom-uploads .upload_image,
.buttons-box p {
margin-top:0;
width:auto;
float:none;
}
.custom-uploads a {
margin-right:12px;
}
.add-slot {
margin:6px 0;
}
</style>
<?php
endif;
}
Code goes in functions.php file of your child theme or in a plugin file. Tested and works.
You will get something like:
So now, additional images can be added or removed from product variations with ease... They are saved for each variation as an array of attachment Ids using a unique custom field.