Поделюсь кодом, который добавляет кнопку «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
- Дополнительно проверяются права пользователя и наличие товара
