Как создать отзывы с фотографиями в WordPress

Отзывы с фотографиями — эффективный способ повысить доверие посетителей к вашему сайту. В этой статье мы подробно разберём, как создать функционал отзывов с прикреплением фотографий в WordPress, используя кастомные типы записей, метаполя и AJAX-загрузку изображений. Такой подход позволит пользователям удобно оставлять отзывы, а вам — управлять ими из админки.

Создание кастомного типа записи «Отзывы»

Для начала создадим собственный тип записи, чтобы отзывы были отделены от обычных постов и страниц. Это удобно для организации и вывода на сайте.

function wpvip_register_reviews_cpt() {
    $labels = [
        'name'               => 'Отзывы',
        'singular_name'      => 'Отзыв',
        'add_new'            => 'Добавить отзыв',
        'add_new_item'       => 'Добавить новый отзыв',
        'edit_item'          => 'Редактировать отзыв',
        'new_item'           => 'Новый отзыв',
        'all_items'          => 'Все отзывы',
        'view_item'          => 'Просмотреть отзыв',
        'search_items'       => 'Поиск отзывов',
        'not_found'          => 'Отзывы не найдены',
        'not_found_in_trash' => 'В корзине отзывов не найдено',
        'menu_name'          => 'Отзывы'
    ];

    $args = [
        'labels'             => $labels,
        'public'             => true,
        'has_archive'        => true,
        'menu_icon'          => 'dashicons-testimonial',
        'supports'           => ['title', 'editor', 'thumbnail'],
        'show_in_rest'       => true,
    ];

    register_post_type('wpvip_review', $args);
}
add_action('init', 'wpvip_register_reviews_cpt');

Этот код добавит тип записи wpvip_review с поддержкой заголовка, контента и миниатюры (фотографии). Для отзывов миниатюра — это одна из фотографий пользователя.

Добавление пользовательских полей для загрузки нескольких фотографий

Чтобы пользователь мог добавить несколько фотографий к отзыву, создадим метаполе для хранения ID загруженных изображений. Используем для этого REST API и AJAX-загрузку, чтобы не перегружать интерфейс.

Регистрация метаполя с REST API поддержкой

function wpvip_register_review_images_meta() {
    register_post_meta('wpvip_review', 'wpvip_review_images', [
        'type'         => 'array',
        'description'  => 'Фотографии отзывов',
        'single'       => true,
        'show_in_rest' => true,
        'default'      => []
    ]);
}
add_action('init', 'wpvip_register_review_images_meta');

Теперь поле wpvip_review_images доступно через REST API и хранит массив ID вложений.

AJAX обработчик загрузки изображений

Создадим AJAX-обработчик, который позволит загружать фотографии из формы отзыва без перезагрузки страницы.

function wpvip_ajax_upload_review_image() {
    if (!current_user_can('upload_files')) {
        wp_send_json_error('Нет прав для загрузки файлов.');
    }

    if (empty($_FILES['review_image'])) {
        wp_send_json_error('Файл не передан.');
    }

    require_once ABSPATH . 'wp-admin/includes/file.php';
    require_once ABSPATH . 'wp-admin/includes/media.php';
    require_once ABSPATH . 'wp-admin/includes/image.php';

    $file = $_FILES['review_image'];
    $attachment_id = media_handle_upload('review_image', 0);

    if (is_wp_error($attachment_id)) {
        wp_send_json_error($attachment_id->get_error_message());
    }

    $image_url = wp_get_attachment_url($attachment_id);

    wp_send_json_success(['attachment_id' => $attachment_id, 'url' => $image_url]);
}
add_action('wp_ajax_wpvip_upload_review_image', 'wpvip_ajax_upload_review_image');
add_action('wp_ajax_nopriv_wpvip_upload_review_image', 'wpvip_ajax_upload_review_image');

Этот код обрабатывает файл из формы по ключу review_image, загружает его в медиа-библиотеку и возвращает ID и URL файла.

Создание формы для добавления отзыва с фотографиями на фронтенде

Теперь соберём форму, позволяющую пользователю оставить отзыв с несколькими фотографиями. Для удобства загрузку реализуем через JavaScript.

HTML и JavaScript формы

<form id="wpvip-review-form" enctype="multipart/form-data">
    <label>Ваше имя:</label>
    <input type="text" name="reviewer_name" required />

    <label>Отзыв:</label>
    <textarea name="review_text" required></textarea>

    <label>Фотографии:</label>
    <input type="file" id="wpvip-review-images" multiple accept="image/*" />
    <div id="wpvip-uploaded-images"></div>

    <button type="submit">Отправить отзыв</button>
</form>

<script>
const form = document.getElementById('wpvip-review-form');
const imagesInput = document.getElementById('wpvip-review-images');
const uploadedImagesContainer = document.getElementById('wpvip-uploaded-images');
let uploadedImageIDs = [];

imagesInput.addEventListener('change', () => {
    const files = imagesInput.files;
    for (let i = 0; i < files.length; i++) {
        const file = files[i];
        const formData = new FormData();
        formData.append('action', 'wpvip_upload_review_image');
        formData.append('review_image', file);
        formData.append('_wpnonce', wpvip_ajax_obj.nonce);

        fetch(wpvip_ajax_obj.ajax_url, {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if(data.success) {
                uploadedImageIDs.push(data.data.attachment_id);
                const img = document.createElement('img');
                img.src = data.data.url;
                img.style.maxWidth = '100px';
                img.style.marginRight = '10px';
                uploadedImagesContainer.appendChild(img);
            } else {
                alert('Ошибка загрузки: ' + data.data);
            }
        })
        .catch(() => alert('Ошибка загрузки изображения'));
    }
    imagesInput.value = '';
});

form.addEventListener('submit', (e) => {
    e.preventDefault();
    const name = form.reviewer_name.value.trim();
    const text = form.review_text.value.trim();

    if (!name || !text) {
        alert('Заполните все обязательные поля');
        return;
    }

    const postData = {
        action: 'wpvip_submit_review',
        reviewer_name: name,
        review_text: text,
        images: uploadedImageIDs,
        _wpnonce: wpvip_ajax_obj.nonce
    };

    fetch(wpvip_ajax_obj.ajax_url, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(postData)
    })
    .then(response => response.json())
    .then(data => {
        if(data.success) {
            alert('Спасибо за отзыв!');
            form.reset();
            uploadedImagesContainer.innerHTML = '';
            uploadedImageIDs = [];
        } else {
            alert('Ошибка при добавлении отзыва: ' + data.data);
        }
    })
    .catch(() => alert('Ошибка отправки отзыва'));
});
</script>

Для работы скрипта нужно локализовать переменную wpvip_ajax_obj с параметрами AJAX в вашем шаблоне или плагине.

Обработка отправки отзыва на сервере и сохранение данных

Теперь добавим обработчик AJAX, который примет данные формы, создаст новый отзыв и сохранит ID фотографий в метаполе.

function wpvip_ajax_submit_review() {
    check_ajax_referer('wpvip_nonce', '_wpnonce');

    $name = sanitize_text_field($_POST['reviewer_name'] ?? '');
    $text = sanitize_textarea_field($_POST['review_text'] ?? '');
    $images = isset($_POST['images']) && is_array($_POST['images']) ? array_map('intval', $_POST['images']) : [];

    if (empty($name) || empty($text)) {
        wp_send_json_error('Заполните все обязательные поля');
    }

    $post_id = wp_insert_post([
        'post_type'   => 'wpvip_review',
        'post_title'  => $name,
        'post_content'=> $text,
        'post_status' => 'pending'
    ]);

    if (is_wp_error($post_id) || !$post_id) {
        wp_send_json_error('Ошибка при сохранении отзыва');
    }

    update_post_meta($post_id, 'wpvip_review_images', $images);

    // Если есть изображения, укажем первую как миниатюру
    if (!empty($images)) {
        set_post_thumbnail($post_id, $images[0]);
    }

    wp_send_json_success('Отзыв успешно добавлен');
}
add_action('wp_ajax_wpvip_submit_review', 'wpvip_ajax_submit_review');
add_action('wp_ajax_nopriv_wpvip_submit_review', 'wpvip_ajax_submit_review');

Вывод отзывов с фотографиями на сайте

Чтобы показать отзывы с фотографиями, создадим WP_Query и выведем отзывы с миниатюрой и содержимым.

$args = [
    'post_type' => 'wpvip_review',
    'post_status' => 'publish',
    'posts_per_page' => 10
];
$reviews = new WP_Query($args);
if ($reviews->have_posts()) {
    echo '<div class="wpvip-reviews">';
    while ($reviews->have_posts()) {
        $reviews->the_post();
        $images = get_post_meta(get_the_ID(), 'wpvip_review_images', true);
        echo '<div class="wpvip-review-item">';
        if (has_post_thumbnail()) {
            echo get_the_post_thumbnail(null, 'thumbnail');
        }
        echo '<h3>' . get_the_title() . '</h3>';
        echo '<div class="wpvip-review-content">' . get_the_content() . '</div>';

        if (!empty($images) && is_array($images)) {
            echo '<div class="wpvip-review-images">';
            foreach ($images as $img_id) {
                echo wp_get_attachment_image($img_id, 'medium');
            }
            echo '</div>';
        }
        echo '</div>';
    }
    echo '</div>';
    wp_reset_postdata();
} else {
    echo 'Отзывы не найдены';
}

Рекомендации по безопасности и оптимизации

Важно проверять права доступа при загрузке файлов, использовать nonce для AJAX-запросов и фильтровать входящие данные. Для ускорения загрузки изображений применяйте lazy loading и оптимизацию изображений через плагины.

Полезные плагины для отзывов с фото

  • Expert Review — мощный плагин для создания отзывов с оценками и фото.
  • My Popup — позволяет показывать отзывы в всплывающих окнах.
  • Clearfy Pro — оптимизация и безопасность, чтобы отзывы загружались быстро и без сбоев.

Используя описанный подход, вы сможете создать удобный и функциональный раздел отзывов с поддержкой фотографий, который повысит доверие пользователей и улучшит конверсию сайта.

Как создать динамический виджет в WordPress
15.11.2025
Как использовать WPCommunity для создания сообществ в WordPress
02.04.2026
WooCommerce: почему не отправляется письмо подтверждения заказа и как это исправить
05.05.2026
Как удалить или изменить URL адрес постов в WordPress без потери SEO
20.02.2026
Как создать автоматические отчёты в WordPress с помощью WPExpertReview
17.12.2025