Добавить функционал «Добавить отзыв» в админке WooCommerce

Поделюсь кодом, который добавляет кнопку «Add review» на странице отзывов в админке WooCommerce, отображает модальное окно с формой выбора товара, и обрабатывает ее через AJAX.

Добавляем кнопку на страницу отзывов

Тут немного JQuery и CSS, так как более изящного способа для добавления кнопки я не нашел.

Обращаю внимание, что тут используется функция render_add_review_form(), ее мы пишем далее.

add_action('admin_head', 'insert_add_review_button');
function insert_add_review_button()
{
  $screen = get_current_screen();
  if (isset($screen->id) && $screen->id === 'product_page_product-reviews') {
    $add_review_text_button = 'Add review';
    ?>
    <script type="text/javascript">
      jQuery(document).ready(function($) {
        // Ищем заголовок страницы, куда можно добавить кнопку
        const sectionTitle = $('.wrap h2');

        if (!sectionTitle.length) {
          return;
        }

        const addReviewBtn = $('<a class="page-title-action page-title-action--add-review" href="#"><?php echo $add_review_text_button; ?></a>');

        setTimeout(() => {
          // Добавляем кнопку после заголовка
          sectionTitle.after(addReviewBtn);
          // sectionTitle.css('display', 'inline-block');
        }, 300);

        addReviewBtn.on('click', function(e) {
          e.preventDefault();
          const contentHtml = <?php echo wp_json_encode(render_add_review_form()); ?>;

          createModal('review-modal', contentHtml, getAjaxReviewForm);
        });

        // Создаем модальное окно
        function createModal(modalClassName = 'review-modal', modalContentHtml = '', callbackFnForm = null) {
          const modal = $(`<div class="modal ${modalClassName}"></div>`);
          const modalContent = $('<div class="modal-content"></div>');
          const modalClose = $('<span class="close">×</span>');
          const modalBody = $('<div class="modal-body"></div>');

          modalBody.append(modalClose);
          modalBody.append(modalContentHtml);
          modalContent.append(modalBody);
          modal.append(modalContent);

          // Добавляем модальное окно на страницу
          $('body').append(modal);
          $('body').css('overflow', 'hidden');

          // Добавляем кнопку закрытия модального окна
          modalClose.on('click', function() {
            modal.remove();
            $('body').css('overflow', '');
          });

          // Отправляем форму
          if (!modalContent.find('form').length) {
            return;
          }

          modalContent.find('form').on('submit', function(e) {
            e.preventDefault();

            if (callbackFnForm) {
              callbackFnForm(e);
            }
          });
        }

        function getAjaxReviewForm(e) {
          $.ajax({
            url: '<?php echo admin_url("admin-ajax.php") ?>',
            type: 'POST',
            data: {
              action: 'add_product_review',
              _wpnonce: $('#add-review-form input[name="nonce"]').val(),
              product_id: $('#add-review-form select[name="product_id"]').val()
            },
            success: function(data) {
              if (data.success) {
                window.location.href = data.edit_url;
              } else {
                console.error(data.error);
                $('#add-review-form').prepend('<div class="error-message" style="color:red;">Something went wrong: ' + data.error + '</div>');
              }
            },
            error: function(xhr, status, error) {
              console.error(error);
              $('#add-review-form').prepend('<div class="error-message" style="color:red;">Something went wrong</div>');
            }
          });
        }
      });
    </script>

    <style>
      .review-modal.modal {
        display: block;
        position: fixed;
        z-index: 9999;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        background-color: rgba(0, 0, 0, 0.4);
      }

      .review-modal .modal-content {
        position: relative;
        background-color: #fefefe;
        margin: 15% auto;
        padding: 20px;
        border: 1px solid #888;
        width: 100%;
        max-width: 300px;
      }

      .review-modal .close {
        color: #aaa;
        float: right;
        font-size: 28px;
        font-weight: bold;
        position: absolute;
        top: 5px;
        right: 5px;
      }

      .review-modal .close:hover,
      .review-modal .close:focus {
        color: black;
        text-decoration: none;
        cursor: pointer;
      }

      #add-review-form {
        display: flex;
        flex-direction: column;
        gap: 10px;
      }

      #add-review-form>* {
        width: 100%;
      }

      #add-review-form label {
        display: flex;
        flex-direction: column;
        gap: 5px;
      }

      #add-review-form label>span {
        font-weight: bold;
      }

      #add-review-form select {
        padding: 5px 10px;
        width: 100%;
        display: block;
      }

      #add-review-form button {
        padding: 5px 10px;
        width: 100%;
        display: block;
        cursor: pointer;
      }

      .page-title-action.page-title-action--add-review {
        display: block !important;
        max-width: 100px;
        text-align: center;
        margin-top: 10px;
      }
    </style>
    <?php
  }
}

Модальное окно с формой выбора товара

Рендерит модальное окно с формой, которое рендерится в коде выше.

function render_add_review_form()
{
  $products = get_posts([
    'post_type' => 'product',
    'numberposts' => -1
  ]);
  
  if (empty($products)) {
    return;
  }

  ob_start();
  ?>

  <form method="post" id="add-review-form">
    <?php wp_nonce_field('add_product_review', 'nonce'); ?>

    <label>
      <span>Add review</span>
      <select name="product_id" required>
        <option value="">Select a product</option>
        <?php foreach ($products as $product): ?>
          <option value="<?php echo esc_attr($product->ID); ?>">
            <?php echo esc_html($product->post_title); ?>
          </option>
        <?php endforeach; ?>
      </select>
    </label>

    <button type="submit">Continue</button>
  </form>

  <?php
  return ob_get_clean();
}

Обработка AJAX

На самом деле тут создается шаблон пустого комментария для выбранного товара и возвращается адрес редактирования товара, на который в дальнейший совершается редирект.

add_action('wp_ajax_bhcr_add_product_review', 'add_product_review_form_to_admin');
function add_product_review_form_to_admin()
{
  if (!wp_verify_nonce($_POST['_wpnonce'], 'add_product_review')) {
    wp_send_json([
      'error' => 'Invalid nonce'
    ], 400);
  }

  if (!current_user_can('manage_woocommerce')) {
    wp_send_json([
      'error' => 'Access denied'
    ], 403);
  }

  if (empty($_POST['product_id'])) {
    wp_send_json([
      'error' => 'Product ID is required'
    ], 400);
  }

  $product_id = absint($_POST['product_id']);
  $product = get_post($product_id);
  if (! $product || $product->post_type !== 'product') {
    wp_send_json(['error' => 'Product not found'], 400);
  }

  // Добавляем отзыв
  $commentdata = [
    'comment_post_ID'      => $product_id,
    'comment_author'       => 'Name',
    'comment_author_email' => 'mail@mail.localhost',
    'comment_content'      => 'Review',
    'comment_type'         => 'review',
    'comment_approved'     => 0, // статус "Ожидает проверки"
  ];

  $comment_id = wp_insert_comment($commentdata);
  if (! $comment_id) {
    wp_send_json(['error' => 'Could not insert review'], 400);
  }

  // Устанавливаем рейтинг
  update_comment_meta($comment_id, 'rating', 5);

  $edit_url = admin_url("comment.php?action=editcomment&c={$comment_id}");
  wp_send_json(['success' => true, 'edit_url' => $edit_url]);
}

Итого

  • Для безопасности формы используются wp_nonce_field и wp_verify_nonce
  • Все работает быстро через AJAX
  • Сама кнопка оформлена в стиле админки WordPress
  • Дополнительно проверяются права пользователя и наличие товара