Делаем ajax отзывы в WordPress, добавляем возможность загружать фото и добавляем рейтинг

Главная » Блог » Делаем ajax отзывы в Wordpress, добавляем возможность загружать фото и добавляем рейтинг

В моем примере я использовал плагин ACF. Но тоже самое можно адаптировать под стандартные произвольные поля. Мои произвольные поля: rating — числовое поле, по умолчанию значение 5. (поле нужно для рейтинга). А так же поле город town.

Шаг 1.

Идем в functions.php и регистрируем новый post_type.

add_action( 'init', 'true_register_post_type_init' ); // Использовать отзыв только внутри хука init

function true_register_post_type_init() {
	$labels = array(
		'name' => 'Отзывы',
		'singular_name' => 'Отзыв', // админ панель Добавить->Отзыв
		'add_new' => 'Добавить отзыв',
		'add_new_item' => 'Добавить новый отзыв', // заголовок тега <title>
		'edit_item' => 'Редактировать отзыв',
		'new_item' => 'Новый отзыв',
		'all_items' => 'Все отзывы',
		'view_item' => 'Просмотр отзыва на сайте',
		'search_items' => 'Искать отзыв',
		'not_found' =>  'Отзывов не найдено.',
		'not_found_in_trash' => 'В корзине нет отзывов.',
		'menu_name' => 'Отзывы' // ссылка в меню в админке
	);
	$args = array(
		'labels' => $labels,
		'public' => true,
		'publicly_queryable' => false,
		'show_ui' => true, // показывать интерфейс в админке
		'has_archive' => false,
		'menu_position' => 20, // порядок в меню
		'supports' => array( 'title', 'editor', 'thumbnail')
	);
	register_post_type('testimonials', $args);
}

Шаг 2.

Здесь же, в functions.php добавляем ниже:

// Вывод уведомления, если есть записи на утверждение
// только для админов
if( current_user_can('manage_options') ){
	add_action( 'admin_notices', 'my_plugin_notice' );
	function my_plugin_notice() {
		$count_posts = wp_count_posts('testimonials');
		$countpanding = $count_posts->pending;// определяем количество записей на утверждении
		if ($countpanding>0) { //если что-то есть, выводим в адмнике уведомление
				?>
				<div class="updated">
					<p style="color:red; font-weight:bold; font-size:14px">Есть новые отзывы на утверждение! Количество: <?php echo $countpanding;?> </p>
					<a href="/wp-admin/edit.php?post_status=pending&post_type=testimonials">Посмотреть</a>
				</div>
				<?php
		}
	}
}

add_action( 'admin_menu', 'add_user_menu_bubble' );
function add_user_menu_bubble(){
	global $menu;

	// записи
	$count = wp_count_posts('testimonials')->pending; // на подтверждении
	if( $count ){
		foreach( $menu as $key => $value ){
			if( $menu[$key][2] == 'edit.php?post_type=testimonials' ){
				$menu[$key][0] .= ' <span class="awaiting-mod"><span class="pending-count">' . $count . '</span></span>';
				break;
			}
		}
	}
}

Этот код отвечает за уведомления в админке.

Шаг 3.

Создаем форму.

<form class="rr_review_form" id="featured_upload" method="post" action="#" enctype="multipart/form-data">
   <input type="hidden" name="rRating" id="rRating" value="0">
   <div class="form-footer clearfix">
      <div class="row">
         <div class="col-md-12">
            <div class="form-group animated-labels">
               <div class="input">
                  <input id="Name" class="form-control required" type="text" name="name" value="" required="required" placeholder="Ваше имя">
               </div>
               <span class="form-err"></span>
            </div>
         </div>
      </div>
      <div class="row">
         <div class="col-md-12">
            <div class="form-group animated-labels">
               <div class="input">
                  <input id="Town" class="form-control required" type="text" name="town" value="" required="required" placeholder="Ваш город">
               </div>
               <span class="form-err"></span>
            </div>
         </div>
      </div>
      <div class="row">
         <div class="col-md-12">
            <div class="form-group animated-labels">
               <div class="input">
                  <textarea id="ReviewContent" class="form-control required" name="text" rows="10" style="resize: none; overflow-y: hidden;" required="required" placeholder="Текст отзыва"></textarea>
               </div>
               <span class="form-err"></span>
            </div>
         </div>
      </div>
      <div class="row" data-sid="RATING" style="    margin-bottom: 20px;
         margin-top: 9px;">
         <div class="col-md-12">
            <label for="POPUP_RATING">Рейтинг</label>
            <div class="input">
               <div class="rating_wrap clearfix">
                  <div class="rating">
                     <span class="star rr_star glyphicon glyphicon-star-empty" data-current_width="20" data-message="Очень плохо" data-rating_value="1" id="rr_star_1"></span>
                     <span class="star rr_star glyphicon glyphicon-star-empty" data-current_width="40" data-message="Плохо" data-rating_value="2" id="rr_star_2"></span>
                     <span class="star rr_star glyphicon glyphicon-star-empty" data-current_width="60" data-message="Нормально" data-rating_value="3" id="rr_star_3"></span>
                     <span class="star rr_star glyphicon glyphicon-star-empty" data-current_width="80" data-message="Хорошо" data-rating_value="4" id="rr_star_4"></span>
                     <span class="star rr_star glyphicon glyphicon-star-empty" data-current_width="100" data-message="Отлично" data-rating_value="5" id="rr_star_5"></span>
                     <span class="stars_current" data-rating="0" style="width: 0%;"></span>
                  </div>
                  <div class="rating_message" data-message="Без оценки"> - Без оценки</div>
               </div>
            </div>
            <span class="form-err"></span>
         </div>
      </div>
      <div class="row">
         <div class="col-md-12">
            <label for="POPUP_RATING">Ваше фото</label>
            <div class="input">
               <input type="file" name="my_image_upload" id="my_image_upload"  multiple="false" />
               <?php wp_nonce_field( 'my_image_upload', 'my_image_upload_nonce' ); ?>
            </div>
            <span class="form-err"></span>
         </div>
      </div>
      <div>
         <button class="btn-lg btn btn-default" id="submitReview" type="button">Отправить</button>
      </div>
      <div class="result"></div>
      <span class="name-field">Вы не заполнили поле Имя</span><br>
      <span class="name-field2">Вы не заполнили поле Текст отзыва</span>
   </div>
</form>

Шаг 4. Добавляем скрипты.

$(document).on('click', '.rr_star', function() {
    var rate = $(this).data('rating_value');
    $('#rRating').val(rate)
});


$(document).on('mouseenter', '.rr_review_form .rating .star', function() {
    var $this = $(this),
        currentStarWidth = $this.data('current_width'),
        ratingValue = $this.data('rating_value'),
        ratingMessage = $this.data('message');
    $this.closest('.rating').find('.stars_current').width(currentStarWidth + '%');
    $this.closest('.rating_wrap').find('.rating_message').text(ratingMessage)
});
$(document).on('mouseleave', '.rr_review_form .rating', function() {
    var $this = $(this),
        dataRating = $this.find('.stars_current').data('rating'),
        ratingMessage = $this.closest('.rating_wrap').find('.rating_message').data('message');
    $this.find('.stars_current').width(dataRating + '%');
    $this.closest('.rating_wrap').find('.rating_message').text(ratingMessage)
});
$(document).on('click', '.rr_review_form .rating .star', function() {
    var $this = $(this),
        currentStarWidth = $this.data('current_width'),
        ratingValue = $this.data('rating_value'),
        ratingMessage = $this.data('message');
    $this.closest('.rating').find('.stars_current').data('rating', currentStarWidth);
    if ($this.closest('.input').find('input[name=RATING]').length) {
        $this.closest('.input').find('input[name=RATING]').val(ratingValue)
    } else {
        $this.closest('.input').find('input[data-sid=RATING]').val(ratingValue)
    }
    $this.closest('.rating_wrap').find('.rating_message').data('message', ratingMessage)
});
$(document).on('click', '#submitReview', function() {
    var name = $('#Name').val();
    var text = $('#ReviewContent').val();
    var rate = $('#rRating').val();
    var my_image_upload = $('#my_image_upload').val();
    if (name == '') {
        $('#Name').addClass('wpcf7-not-valid');
        $('.name-field').addClass('d-block');
    } else {
        $('#Name').removeClass('wpcf7-not-valid');
        $('.name-field').removeClass('d-block');
    }

    if (text == '') {
        $('#ReviewContent').addClass('wpcf7-not-valid');
        $('.name-field2').addClass('d-block');
    } else {
        $('#ReviewContent').removeClass('wpcf7-not-valid');
        $('.name-field2').removeClass('d-block');
    }

    if (name !== '' && text !== '') {
        $.ajax({
            url: '/wp-content/themes/kids/ajaxupload.php',
            type: 'POST',
            data: new FormData(document.getElementById("featured_upload")),
            contentType: false,
            cache: false,
            processData: false,
            success: function(data) {
                $('.result').text('Спасибо! Ваш отзыв отправлен на модерацию.');
                $('#submitReview').attr('disabled', true);
            }
        })
    }
});

Здесь сразу и события на звездочки рейтинга и отправка самой формы в обработчик ajaxopload.php

Шаг 5. Обработчик ajaxopload.php

<?php

$parse_uri = explode( 'wp-content', $_SERVER['SCRIPT_FILENAME'] );
require_once( $parse_uri[0] . 'wp-load.php' );

 $post_title = $_POST['name'];
 $post_content = $_POST['text'];
 $rate = $_POST['rate'];
 $town = $_POST['town'];

 $new_post = array(
'post_type' => 'testimonials',
 'post_content' => $post_content,
 'post_title' => $post_title,
 'post_status' => 'pending',
 );

 $post_id = wp_insert_post($new_post);


if (
	isset( $_POST['my_image_upload_nonce'], $post_id )
	&& wp_verify_nonce( $_POST['my_image_upload_nonce'], 'my_image_upload' )
) {
	// все ок! Продолжаем.
	// Эти файлы должны быть подключены в лицевой части (фронт-энде).
	require_once( ABSPATH . 'wp-admin/includes/image.php' );
	require_once( ABSPATH . 'wp-admin/includes/file.php' );
	require_once( ABSPATH . 'wp-admin/includes/media.php' );

	// Позволим WordPress перехватить загрузку.
	// не забываем указать атрибут name поля input - 'my_image_upload'
	$attachment_id = media_handle_upload( 'my_image_upload', $post_id );

	if ( is_wp_error( $attachment_id ) ) {
		echo "Ошибка загрузки медиафайла.";
	} else {
		echo "Медиафайл был успешно загружен!";
	}

} else {
	echo "Проверка не пройдена. Невозможно загрузить файл.";
}

if( set_post_thumbnail($post_id, $attachment_id) )
	echo 'Миниатюра установлена.';
else
	echo 'Миниатюра удалена.';

update_field('rating', $rate, $post_id );

update_field('town', $town, $post_id );

$to  = "почтаадмина@mail.ru";

$subject = "Новый отзыв ожидает вашей проверки";

$message = ' <p>Имя '.$post_title.'</p> </br> Текст отзыва: </br> '.$post_content.' ';

$headers  = "Content-type: text/html; charset=utf-8 \r\n";
$headers .= "From: <info@вашадоменнаяпочта.ru>\r\n";

mail($to, $subject, $message, $headers);

?>

Этот скрипт создает запись в нашем post_type и отправляет данные на почту.

Шаг 6. Причесываем CSS

.rr_review_name {
  color: #f68315;
  font-family: MontserratBold;
  font-size: 15px;
  font-weight: 700;
  line-height: 21px;
}

.text p {
  color: #717171;
  font-size: 16px;
  line-height: 24px;
  margin-top: 25px;
  font-family: 'MontserratItalic';
  max-width: 80%;
}

.stars_current {
  width: 80%;
  font-size: 0;
  margin-top: 5px;
  display: block;
  color: #e3c45a;
  letter-spacing: 3px;
}

.add_review button {
  width: 225px;
  height: 50px;
  background-color: #613085;
  background-image: none;
  font-size: 14px;
  margin-bottom: 30px;
}

.add_review {
  margin-top: 85px;
}

#exampleModalCenter3 .modal-dialog {
  max-width: 400px;
}

.rr_review_form input {
  color: #717171;
  font-size: 14px;
  font-weight: 400;
  line-height: 24px;
  padding-left: 26px;
  overflow: visible;
  width: 100%;
  height: 50px;
  border: 1px solid #c3c3c3;
  border-radius: 30px;
  background-color: transparent;
}

.rr_review_form textarea {
  color: #717171;
  font-size: 14px;
  font-weight: 400;
  line-height: 24px;
  padding-left: 26px;
  overflow-y: auto !important;
  width: 100%;
  height: 155px;
  border: 1px solid #c3c3c3;
  border-radius: 30px;
  background-color: transparent;
  margin-right: 0;
  max-width: 100%;
}

.form-control:focus {
  color: #495057;
  background-color: #fff;
  border-color: #613085;
  outline: 0;
  box-shadow: 0 0 0 0.2rem rgba(97, 48, 133, 0.25);
}

form .row[data-sid=RATING] .rating {
  /* float: left; */
  position: relative;
  width: 150px;
  height: 24px;
  font-size: 0;
  line-height: 0;
  background: url(../img/bigstars.svg) 0 -26px no-repeat;
  margin-right: 8px;
  margin-top: -2px;
  display: flex;
  display: inline-block;
}

form .row[data-sid=RATING] .rating .star {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0;
  height: 24px;
  display: inline-block;
  width: 20%;
  background: none;
  cursor: pointer;
  border-radius: 0;
}

.stars, .rr_star {
  color: #ffaf00;
}

.rating .stars_current {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  display: block;
  width: 0;
  background: url(../img/bigstars.svg) 0 0 no-repeat;
  margin: 0;
}

.rating_message {
  display: inline-block;
  position: relative;
  top: -5px;
}

.item-views.reviews_items .items .item .rating_wrap {
  margin: 0;
  display: inline-block;
  vertical-align: top;
  margin-top: 15px;
}

.rating {
  position: relative;
  width: 121px;
  height: 19px;
  font-size: 0;
  line-height: 0;
  background: url(../img/stars.svg) 0 -21px no-repeat;
}

.stars .rating .stars_current {
  background: url(../img/stars.svg) 0 0 no-repeat;
}

.rr_review_form .row {
  color: #717171;
  font-size: 14px;
}

Я в тупую скопировал стили с сайта примера. Поэтому редактируйте их сами. Самое важное что здесь нужно это stars.svg и bigstars.svg без них у вас тупо не будут отображаться звездочки.

Шаг 7. Вывод комментариев

<div class="add_review">
            <div class="button add_review_icon">
              <button class="btn btn-default btn-lg animate-load" data-toggle="modal" data-target="#exampleModalCenter3">Оставить свой отзыв</button>
            </div>
          </div>
          <div class="item-views reviews_items">
            <div class="group-content">
              <div class="sid- items">
                <?php
                $args = array(
                'post_type' => 'testimonials',
                'publish' => true,
                );
                query_posts($args);?>
                <div class="testimonial_group">
                  <?php if (have_posts()) : ?>
                  <?php while (have_posts()) : the_post(); ?>
                  <div class="col-md-12 item question1" itemscope="" itemtype="http://schema.org/Review">

                    <div class="d-flex">
                      <div class="photo" style="background-image:url('<?php the_post_thumbnail_url('medium'); ?>');"></div>
                      <div class="">
                      <span itemprop="itemReviewed" itemscope="" itemtype="http://schema.org/Product">
                        <div class="rr_review_post_id" itemprop="name" style="display:none;">
                          <a href="https://kidsprofiki.com/otzyvy/" tabindex="-1">
                          Отзывы
                        </a>
                        </div>
                      </span>
                    <span class="rr_date" style="display:none;"><meta itemprop="datePublished" content="2019-02-27 14:04:04">
                      <time datetime="">
                      </time>
                    </span>
                    <div class="rr_review_name" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
                      <span itemprop="name"><?php the_title(); ?>	</span>
                    </div>
                    <span class="townn">г. <?php the_field('town'); ?></span>
                    <div class="top-wrapper">
                      <div class="rating_wrap">
                        <div class="stars">
                          <div class="rating current_5">
                            <span class="stars_current" style="<?php
                              $rate = get_field('rating');
                              switch ($rate) {
                              case 0:
                              echo "width: 0%;";
                              break;
                              case 1:
                              echo "width: 20%;";
                              break;
                              case 2:
                              echo "width: 40%;";
                              break;
                              case 3:
                              echo "width: 60%;";
                              break;
                              case 4:
                              echo "width: 80%;";
                              break;
                              case 5:
                              echo "width: 100%;";
                              break;
                              }
                            ?>">
                              <?php
                                for($i=1;$i<=$rate;$i++)
                                {
                                print '★';
                                }
                              ?>
                            </span>
                          </div>
                        </div>
                      </div>
                    </div>
                    <div style="display:none;" itemprop="reviewRating" itemscope="" itemtype="http://schema.org/Rating">
                      <span itemprop="ratingValue">
                        5
                      </span>
                      <span itemprop="bestRating">
                        5
                      </span>
                      <span itemprop="worstRating">
                        1
                      </span>
                    </div>
                    <div class="text"><span itemprop="reviewBody"><?php the_content(); ?></span></div>
                    </div>
                    </div>
                    </div>
                    <?php endwhile; ?>
                    <?php endif; ?>
                  </div>
                </div>
              </div>
        </div>

Опять же, просто скопировал свою верстку. Стилизуйте и смотрите сами как вам надо.

Рабочий пример на сайте клиента: https://kidsprofiki.com/otzyvy/

Забыл сказать, что отзыв отправляется на модерацию и его надо еще опубликовать, чтоб он появился.

P.S. Я не претендую на лавры крутого программиста. Да, можно по-другому. Да, можно проще. Но я делюсь с вами своим решением. Возможно когда-то я все это переделаю. А пока, можете сами под себя переделывать сколько угодно )