Отзывы в WordPress без Плагинов

Рассмотрим, как сделать добавление и отправку отзывов в WordPress. При отправке отзыва пользователем он будет добавлен как запись со статусом «На утверждении». Отзыв будет размещён на сайте после проверки/модерации администратором. Рассмотрим также вариант, чтобы отзыв публиковался автоматически, после его отправки пользователем.

Файловая структура. Необходимые файлы.

  • Папка темы/
    • includes/
      • add_review.php
      • loop-review.php
    • js
      • add_review.js
    • functions.php
    • page-review.php

Форма отправки отзывов

Создадим форму отправки отзывов на сайте.

Обратите внимание на id формы add_review. Т.к. далее мы будем слушать событие submit - отправки формы.

<form id="add_review">
    <h3>Добавление отзыва</h3>
    <input type="text" name="name" placeholder="Ваше Имя" required>
    <textarea name="message" placeholder="Ваше сообщение" required></textarea>
    <div class="rating__group">
        <input class="rating__star" type="radio" name="rating" value="1" aria-label="Ужасно">
        <input class="rating__star" type="radio" name="rating" value="2" aria-label="Сносно">
        <input class="rating__star" type="radio" name="rating" value="3" aria-label="Нормально">
        <input class="rating__star" type="radio" name="rating" value="4" aria-label="Хорошо">
        <input class="rating__star" type="radio" name="rating" value="5" aria-label="Отлично" checked>
    </div>
</form>

Стилизация рейтинга.

// Звёздный рейтинг для отзывов

$star_width: 22px         // размер звезды

$star_default: 23DADADA   // цвет по умолчанию

$star_checked: 2310C8D2   // цвет при наведении/выбор



.rating__group

    position: relative

    width: calc(#{$star_width} * 5)

    height: $star_width

    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' %3E%3Cpath style='fill:%#{$star_default}' d='M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z'/%3E%3C/svg%3E")

    background-size: $star_width auto

    background-repeat: repeat-x

    background-color: transparent



.rating__star

    appearance: none

    position: absolute

    top: 0

    left: 0

    height: $star_width

    margin: 0

    font-size: inherit

    background-size: $star_width auto

    background-repeat: repeat-x

    background-color: transparent

    cursor: pointer

    opacity: 1

    &:focus

        outline: none

    &:checked

        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' %3E%3Cpath style='fill:%#{$star_checked}' d='M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z'/%3E%3C/svg%3E")

        width: $star_width

        height: $star_width

        background-size: $star_width

    &:hover

        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' %3E%3Cpath style='fill:%#{$star_checked}' d='M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z'/%3E%3C/svg%3E")

        width: $star_width

        height: $star_width

        background-size: $star_width

        ~ .rating__star

            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath style='fill:%#{$star_default};' d='M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z'%3E%3C/path%3E%3C/svg%3E")

    &:nth-child(1)

        width: $star_width

        z-index: 5

    &:nth-child(2)

        width: calc(#{$star_width} * 2)

        z-index: 4

    &:nth-child(3)

        width: calc(#{$star_width} * 3)

        z-index: 3

    &:nth-child(4)

        width: calc(#{$star_width} * 4)

        z-index: 2

    &:nth-child(5)

        width: calc(#{$star_width} * 5)

        z-index: 1

Создание произвольного типа записи «Отзывы»

Создадим произвольный тип записи для отзывов в functions.php.

/**
 * Новый тип записи - «Отзывы»
**/
add_action( 'init', 'register_post_type_reviews' );
function register_post_type_reviews(){
    register_post_type('reviews', array(
        'label'  => null,
        'labels' => [
            'name'               => 'Отзывы',
            'singular_name'      => 'Отзыв',
            'add_new'            => 'Добавить отзыв',
            'add_new_item'       => 'Добавление отзыва',
            'edit_item'          => 'Редактирование отзыва',
            'new_item'           => 'Новый отзыв',
            'view_item'          => 'Смотреть отзыв',
            'search_items'       => 'Искать отзывы',
            'not_found'          => 'Не найдено',
            'not_found_in_trash' => 'Не найдено в корзине',
            'menu_name'          => 'Отзывы',
        ],
        'description'            => 'Отзывы',
        'exclude_from_search'    => false,
        'public'                 => true,
        'capability_type'        => 'page',
        'hierarchical'           => false,
        'show_in_menu'           => null,
        'show_in_rest'           => null,
        'rest_base'              => null,
        'menu_position'          => null,
        'menu_icon'              => 'dashicons-format-status',
        'supports'               => [
            'title',
            'editor',
            // 'excerpt',

            // 'trackbacks',

            // 'custom-fields',

            // 'comments',

            // 'revisions',

            // 'thumbnail',

            // 'author',

            // 'page-attributes',

        ],
        'has_archive'         => false,
        'rewrite'             => true,
        'query_var'           => true,
    ) );
}

/**
 * Уведомления о новых неопубликованных отзывах
**/
add_action( 'admin_menu', 'add_user_menu_bubble' );
function add_user_menu_bubble() {
    global $menu;

    $count = wp_count_posts('reviews')->pending; # на утверждении

    if ($count) {
        foreach ($menu as $key => $value) {
            if ( $menu[$key][2] == 'edit.php?post_type=reviews' ) {
                $menu[$key][0] .= '<span class="awaiting-mod"><span class="pending-count">'.$count.'</span></span>';
                break;
            }
        }
    }
}

Файл обработчик формы создания отзывов

Теперь нам необходимо создать файл-обработчик для нашей формы отправки отзывов. Назовём его add_review.js.

Или же вы можете добавить данный код в уже существующий файл js.

/**
 * Добавление отзыва
**/
$('#add_review').submit(function (e) {
    e.preventDefault();
    $.ajax({
        type: 'POST',
        url: '/wp-content/themes/{Название вашей темы}/includes/add_review.php',
        data: $(this).serialize(),
        success: () => {
            console.log('Спасибо. Ваш отзыв отправлен.');
            $(this).trigger('reset'); // очищаем поля формы

        },
        error: () => console.log('Ошибка отправки.');
    });
});

Получение и добавление отзыва в базу

Теперь нам необходимо обработать отправленный отзыв на стороне сервера. Создадим файл add_review.php.

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);

// Подключаем необходимые файлы

require_once( $_SERVER['DOCUMENT_ROOT'].'/wp-load.php');
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );

// Получение отправленных данных

$user_name    = trim($_POST['name']);
$user_message = trim($_POST['message']);
$user_rating  = trim($_POST['rating']);
// $review_type  = trim($_POST['review_type']); # можно передать термин таксономии


$post_data = array(
    'post_author'   => 1,
    'post_status'   => 'pending',               # статус - «На утверждении»

    'post_type'     => 'reviews',               # тип записи - «Отзывы»

    'post_title'    => 'Отзыв - ' . $user_name, # заголовок отзыва

    'post_content'  => $user_message,           # текст отзыва

    // 'tax_input'  => ['{Название таксономии}' => array($review_type)],

);

// Вставляем запись в базу данных

$post_id = wp_insert_post( $post_data );

// Добавляем остальные поля

update_field( 'rejting', $user_rating, $post_id ); # рейтинг

update_field( 'name', $user_name, $post_id );      # имя


// Необходимо для записи таксономии «tax_input»

// wp_set_object_terms( $post_id, $review_type, '{Название таксономии}' );

Чтобы отправленный отзыв публиковался незамедлительно, измените 'post_status' на 'publish'.

Последние две строчки обновляют произвольные поля, созданные, например, в Advanced Custom Fields.

rejting, name - это имена произвольных полей ACF.

А текст отзыва мы записываем в текст записи WordPress.

Вывод отзывов

Теперь нам осталось вывести опубликованные отзывы на соответствующей странице, пусть это будет шаблон page-reviews.php - страница с отзывами.

Выведем отзывы с помощью WP_Query.

<?php
$mypost_Query = new WP_Query( array(
    'post_type'      => 'reviews', # тип записи

    'post_status'    => 'publish', # статус записи

    'posts_per_page' => -1,        # количество (-1 - все)

) );

if ( $mypost_Query->have_posts() ) {
    while ( $mypost_Query->have_posts() ) { $mypost_Query->the_post();

        get_template_part('./includes/loop-review'); // шаблон отзыва


    } wp_reset_postdata(); // "сброс"


} else { echo '<p>Извините, пока нет отзывов...</p>'; } ?>

Шаблон отзыва WordPress

Пример шаблона отзыва loop-review.php.

<div class="review-item">
    <div class="review-item__name"><?php the_field('name'); ?></div>
    <div class="rating">
        <?php for ($r = 1; $r <= 5; $r++) { ?>
            <?php if (get_field('rejting') < $r) { ?>
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                    <path style="fill:#DADADA" d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/>
                </svg>
            <?php } else { ?>
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                    <path style="fill:#EC3955" d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/>
                </svg>
            <?php } ?>
        <?php } ?>
    </div>
    <time class="review-item__date"><?php the_date('j.n.Y'); ?></time>
    <div class="review-item__text"><?php the_content(); ?></div>
</div>
  • <?php the_field('name'); ?> - выводим имя человека, написавшего отзыв;
  • <?php get_field('rejting'); ?> - получение рейтинга;
  • <?php the_date('j.n.Y'); ?> - вывод даты публикации отзыва;
  • <?php the_content(); ?> - вывод текста отзыва.
admin