Вовлечённость клиентов — ключевой фактор успешного интернет-магазина. Один из лучших способов повысить доверие к товарам — позволить покупателям добавлять фото к своим отзывам. В этой статье я покажу, как реализовать функционал загрузки изображений в отзывах WooCommerce.
В стандартных отзывах о товарах WooCommerce нет возможности загружать фото, а иногда это бывает необходимо.
Реализованный функционал позволяет:
✅ Загружать до 10 изображений к отзыву
✅ Просматривать фото в удобном слайдере
✅ Увеличивать изображения в Lightbox
✅ Управлять фотографиями из админ-панели
Ниже приведено готовое решение, которое вы можете поместить в function.php вашей темы либо как отдельный сниппет в плагине Code Snippets.
Сниппет целиком
// Добавляем поле загрузки изображений в форму отзывов
add_filter('comment_form_field_comment', 'add_image_upload_to_review_form');
function add_image_upload_to_review_form($field) {
global $post;
if ($post->post_type === 'product') {
$field .= '<p class="comment-form-images"><label for="images"><span class="upload-text">' . __('Добавить фото', 'textdomain') . '</span><span class="file-count-badge">0</span></label><input type="file" id="images" name="images[]" multiple="multiple" accept="image/*" /></p>';
}
return $field;
}
// Устанавливаем enctype для формы комментариев
add_action('wp_footer', 'set_comment_form_enctype');
function set_comment_form_enctype() {
global $post;
if ($post && $post->post_type === 'product') {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var form = document.getElementById('commentform');
if (form) {
form.setAttribute('enctype', 'multipart/form-data');
}
});
</script>
<?php
}
}
// Обрабатываем загрузку изображений при отправке отзыва
add_action('comment_post', 'handle_review_image_uploads', 10, 2);
function handle_review_image_uploads($comment_id, $comment_approved) {
if (isset($_FILES['images']) && !empty($_FILES['images']['name'][0])) {
$uploaded_images = array();
$files = $_FILES['images'];
$max_images = 10;
$file_count = count($files['name']);
if ($file_count > $max_images) {
$file_count = $max_images;
}
for ($i = 0; $i < $file_count; $i++) {
if ($files['name'][$i]) {
$file = array(
'name' => $files['name'][$i],
'type' => $files['type'][$i],
'tmp_name' => $files['tmp_name'][$i],
'error' => $files['error'][$i],
'size' => $files['size'][$i]
);
$_FILES = array('upload' => $file);
$upload = wp_handle_upload($file, array('test_form' => false));
if ($upload && !isset($upload['error'])) {
$uploaded_images[] = $upload['url'];
}
}
}
if (!empty($uploaded_images)) {
update_comment_meta($comment_id, 'review_images', $uploaded_images);
}
}
}
// Отображаем изображения в слайдере над текстом отзыва
add_action('woocommerce_review_before_comment_text', 'display_review_images_slider', 10, 1);
function display_review_images_slider($comment) {
$images = get_comment_meta($comment->comment_ID, 'review_images', true);
if ($images && is_array($images)) {
echo '<div class="review-images-slider">';
echo '<button class="slider-prev"><span class="dashicons dashicons-arrow-left-alt2"></span></button>';
echo '<div class="slider-container">';
foreach ($images as $image) {
echo '<div><img src="' . esc_url($image) . '" alt="Review Image" /></div>';
}
echo '</div>';
echo '<button class="slider-next"><span class="dashicons dashicons-arrow-right-alt2"></span></button>';
echo '</div>';
}
}
// Добавляем стили для слайдера и lightbox
add_action('wp_head', 'add_review_images_styles');
function add_review_images_styles() {
if (is_product()) {
?>
<style>
.review-images-slider {
position: relative;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.slider-container {
display: flex;
overflow-x: auto;
scroll-behavior: smooth;
width: calc(100% - 60px); /* Учитываем ширину кнопок */
margin: 0 30px; /* Отступы для кнопок */
}
.slider-container > div {
flex: 0 0 auto;
width: 100px;
margin-right: 10px;
}
.slider-container img {
cursor: pointer;
}
.slider-prev, .slider-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgb(175 138 120);
color: white;
border: none;
cursor: pointer;
padding: 5px;
z-index: 1;
border-radius: 50px;
}
.slider-prev {
left: 0;
}
.slider-next {
right: 0;
}
#review-lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
z-index: 9999;
}
#lightbox-content {
position: absolute;
top: 300px;
bottom: 300px;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: center;
align-items: center;
width: 80%;
}
.image-container {
position: relative;
display: inline-block;
}
#lightbox-image {
max-width: 100%;
max-height: 700px;
}
#lightbox-close {
position: absolute;
top: 10px;
right: 10px;
color: white;
font-size: 24px;
cursor: pointer;
z-index: 1;
}
#lightbox-prev, #lightbox-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: white;
font-size: 30px;
cursor: pointer;
z-index: 99;
}
#lightbox-prev {
left: 10px;
}
#lightbox-next {
right: 10px;
}
.comment-form-images input[type="file"] {
display: none;
}
.comment-form-images label {
position: relative;
display: inline-block;
background-color: #AF8A78;
color: white;
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;
}
.file-count-badge {
position: absolute;
top: -10px;
right: -10px;
background-color: #846858;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
font-size: 12px;
display: none;
}
</style>
<?php
}
}
// Добавляем JavaScript для слайдера и lightbox
add_action('wp_footer', 'add_review_images_script');
function add_review_images_script() {
if (is_product()) {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var fileInput = document.querySelector('input[name="images[]"]');
var badge = document.querySelector('.file-count-badge');
fileInput.addEventListener('change', function() {
var count = this.files.length;
if (count > 10) {
alert('You can upload a maximum of 10 images.');
this.value = '';
badge.style.display = 'none';
} else if (count > 0) {
badge.textContent = count;
badge.style.display = 'block';
} else {
badge.style.display = 'none';
}
});
// Слайдер
var sliders = document.querySelectorAll('.review-images-slider');
sliders.forEach(function(slider) {
var container = slider.querySelector('.slider-container');
var prev = slider.querySelector('.slider-prev');
var next = slider.querySelector('.slider-next');
var scrollAmount = 110; // ширина изображения + отступ
prev.addEventListener('click', function() {
container.scrollLeft -= scrollAmount;
});
next.addEventListener('click', function() {
container.scrollLeft += scrollAmount;
});
});
// Lightbox
var lightbox = document.getElementById('review-lightbox');
var lightboxImage = document.getElementById('lightbox-image');
var lightboxClose = document.getElementById('lightbox-close');
var lightboxPrev = document.getElementById('lightbox-prev');
var lightboxNext = document.getElementById('lightbox-next');
var currentImages = [];
var currentIndex = 0;
document.querySelectorAll('.review-images-slider img').forEach(function(img) {
img.addEventListener('click', function() {
var slider = this.closest('.review-images-slider');
currentImages = Array.from(slider.querySelectorAll('img')).map(function(img) { return img.src; });
currentIndex = currentImages.indexOf(this.src);
lightboxImage.src = this.src;
lightbox.style.display = 'block';
});
});
lightboxClose.addEventListener('click', function() {
lightbox.style.display = 'none';
});
lightboxPrev.addEventListener('click', function() {
if (currentIndex > 0) {
currentIndex--;
lightboxImage.src = currentImages[currentIndex];
}
});
lightboxNext.addEventListener('click', function() {
if (currentIndex < currentImages.length - 1) {
currentIndex++;
lightboxImage.src = currentImages[currentIndex];
}
});
// Закрытие lightbox при клике вне изображения
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox) {
lightbox.style.display = 'none';
}
});
});
</script>
<?php
}
}
// Добавляем HTML для lightbox
add_action('wp_footer', 'add_lightbox_html');
function add_lightbox_html() {
if (is_product()) {
?>
<div id="review-lightbox">
<div id="lightbox-content">
<button id="lightbox-prev"><span class="dashicons dashicons-arrow-left-alt2"></span></button>
<div class="image-container">
<img id="lightbox-image" src="" />
<span id="lightbox-close">×</span>
</div>
<button id="lightbox-next"><span class="dashicons dashicons-arrow-right-alt2"></span></button>
</div>
</div>
<?php
}
}
// Добавляем мета-бокс в админ-панели для отзывов
add_action('add_meta_boxes_comment', 'add_review_images_meta_box');
function add_review_images_meta_box($comment) {
if ($comment->comment_type === 'review') {
add_meta_box('review-images', 'Review Images', 'display_review_images_meta_box', 'comment', 'normal', 'high');
}
}
function display_review_images_meta_box($comment) {
$images = get_comment_meta($comment->comment_ID, 'review_images', true);
if ($images && is_array($images)) {
echo '<div style="display: flex; flex-wrap: wrap;">';
foreach ($images as $image) {
echo '<div style="position: relative; margin: 10px;">';
echo '<img src="' . esc_url($image) . '" style="max-width:100px;" />';
echo '<span class="delete-review-image" data-image="' . esc_attr($image) . '" data-comment-id="' . $comment->comment_ID . '" style="position: absolute; top: 0; right: 0; cursor: pointer; color: red; font-size: 20px;">×</span>';
echo '</div>';
}
echo '</div>';
} else {
echo 'No images uploaded.';
}
}
// Добавляем скрипт для удаления изображений в админ-панели
add_action('admin_footer', 'add_review_images_admin_script');
function add_review_images_admin_script() {
if (get_current_screen()->id === 'comment') {
?>
<script>
jQuery(document).ready(function($) {
$(document).on('click', '.delete-review-image', function(e) {
e.preventDefault();
var image = $(this).data('image');
var commentId = $(this).data('comment-id');
if (confirm('Are you sure you want to delete this image?')) {
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
data: {
action: 'delete_review_image',
image: image,
comment_id: commentId,
nonce: '<?php echo wp_create_nonce('delete_review_image'); ?>'
},
success: function(response) {
if (response.success) {
$(this).parent().remove();
} else {
alert('Failed to delete image.');
}
}.bind(this)
});
}
});
});
</script>
<?php
}
}
// Обрабатываем AJAX-запрос для удаления изображения
add_action('wp_ajax_delete_review_image', 'delete_review_image');
function delete_review_image() {
check_ajax_referer('delete_review_image', 'nonce');
$comment_id = intval($_POST['comment_id']);
$image_to_delete = sanitize_text_field($_POST['image']);
$images = get_comment_meta($comment_id, 'review_images', true);
if ($images && is_array($images)) {
$key = array_search($image_to_delete, $images);
if ($key !== false) {
unset($images[$key]);
update_comment_meta($comment_id, 'review_images', array_values($images));
$upload_dir = wp_upload_dir();
$file_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $image_to_delete);
if (file_exists($file_path)) {
unlink($file_path);
}
wp_send_json_success();
} else {
wp_send_json_error();
}
} else {
wp_send_json_error();
}
}
// Подключаем Dashicons на страницах товаров
add_action('wp_enqueue_scripts', 'enqueue_dashicons_for_reviews');
function enqueue_dashicons_for_reviews() {
if (is_product()) {
wp_enqueue_style('dashicons');
}
}
Как это работает
1. Добавляем поле загрузки изображений
С помощью хука comment_form_field_comment
добавляем поле для загрузки файлов в форму отзывов:
add_filter('comment_form_field_comment', 'add_image_upload_to_review_form');
function add_image_upload_to_review_form($field) {
global $post;
if ($post->post_type === 'product') {
$field .= '<p class="comment-form-images"><label for="images"><span class="upload-text">' . __('Добавить фото', 'textdomain') . '</span><span class="file-count-badge">0</span></label><input type="file" id="images" name="images[]" multiple="multiple" accept="image/*" /></p>';
}
return $field;
}
2. Настраиваем форму для загрузки файлов
Добавляем атрибут enctype="multipart/form-data"
к форме комментариев:
add_action('wp_footer', 'set_comment_form_enctype');
function set_comment_form_enctype() {
global $post;
if ($post && $post->post_type === 'product') {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var form = document.getElementById('commentform');
if (form) {
form.setAttribute('enctype', 'multipart/form-data');
}
});
</script>
<?php
}
}
3. Обрабатываем загруженные изображения
При сохранении комментария обрабатываем загруженные файлы:
add_action('comment_post', 'handle_review_image_uploads', 10, 2);
function handle_review_image_uploads($comment_id, $comment_approved) {
if (isset($_FILES['images']) && !empty($_FILES['images']['name'][0])) {
// Код обработки изображений
// ...
update_comment_meta($comment_id, 'review_images', $uploaded_images);
}
}
4. Отображаем изображения в отзывах
Создаём красивый слайдер для просмотра фотографий отзывов:
add_action('woocommerce_review_before_comment_text', 'display_review_images_slider', 10, 1);
function display_review_images_slider($comment) {
$images = get_comment_meta($comment->comment_ID, 'review_images', true);
if ($images && is_array($images)) {
echo '<div class="review-images-slider">';
// Код слайдера
echo '</div>';
}
}
5. Добавляем Lightbox для полноэкранного просмотра
Реализуем удобный просмотр фотографий в модальном окне:
document.querySelectorAll('.review-images-slider img').forEach(function(img) {
img.addEventListener('click', function() {
// Код открытия Lightbox
});
});
Стилизация элементов
Мы добавили CSS для:
- Красивой кнопки загрузки файлов
- Адаптивного слайдера изображений
- Модального окна (Lightbox)
- Индикатора количества выбранных файлов
.review-images-slider {
position: relative;
margin-bottom: 10px;
display: flex;
align-items: center;
}
/* Другие стили... */
Управление изображениями в админке
Для удобства модерации добавлен метабокс в админ-панели с возможностью удаления фотографий:
add_action('add_meta_boxes_comment', 'add_review_images_meta_box');
function add_review_images_meta_box($comment) {
if ($comment->comment_type === 'review') {
add_meta_box('review-images', 'Review Images', 'display_review_images_meta_box', 'comment', 'normal', 'high');
}
}