Вопрос или проблема
Я пишу фильтр продуктов и столкнулся с этой проблемой.
У меня есть 2 разных атрибута в фильтре продукта: Наклон (Tilt) — в виде флажка с «Да» или «Нет», и Совместимость с несколькими брендами (Multibrand Compatible) — также в виде флажка с «Да» или «Нет». Если я нажимаю Совместимость с несколькими брендами «Нет», продукты без атрибута Совместимости с несколькими брендами не отображаются.
Допустим, сначала я нажимаю на Совместимость с несколькими брендами с атрибутом «Нет». Если затем я нажимаю на Наклон с атрибутом «Да», должны отображаться продукты, которые имеют наклон и НЕ являются совместимыми с несколькими брендами.
Вместо этого я получаю все продукты с наклоном, включая те, которые имеют положительную совместимость с несколькими брендами.
URL-адрес:
/?multibrand=no&tilt=yes
Вот мой HTML:
function multiplan_compatible_filter() {
// Начать буферизацию вывода, чтобы захватить HTML-вывод
ob_start();
?>
<!-- HTML-структура для выпадающего фильтра Совместимости с несколькими брендами -->
<div id="multibrand-tilt-filter-container" class="multibrand-dropdown">
<div class="multibrand-dropdown-wrapper">
<button class="multibrand-dropdown-btn">Совместимость с несколькими брендами ▼</button>
<div class="multibrand-dropdown-content">
<label>
<input type="checkbox" id="multibrand-yes" value="yes" data-attribute="pa_multibrand-compatible">
Да
</label>
<label>
<input type="checkbox" id="multibrand-no" value="no" data-attribute="pa_multibrand-compatible">
Нет
</label>
</div>
</div>
</div>
Вот основной Javascript и AJAX:
<!-- JavaScript для обработки функционала фильтра -->
<script>
jQuery(function($) {
// Переключение видимости выпадающего списка при нажатии кнопки
$('.multibrand-dropdown-btn').click(function(e) {
e.stopPropagation();
$('.multibrand-dropdown-content').toggleClass('show');
});
// Закрыть выпадающий список при нажатии вне его
$(window).click(function() {
$('.multibrand-dropdown-content, .tilt-dropdown-content').removeClass('show');
});
// Обработать изменения флажков
$('#multibrand-yes, #multibrand-no').on('change', function() {
const currentCheckbox = $(this);
const isChecked = currentCheckbox.is(':checked');
const isYesFilter = currentCheckbox.attr('id') === 'multibrand-yes';
// Обновить URL параметр
let currentUrl = new URL(window.location.href);
let params = currentUrl.searchParams;
params.delete('multibrand');
if (isChecked) {
params.set('multibrand', isYesFilter ? 'yes' : 'no');
}
history.pushState(null, '', '?' + params.toString());
// Убедиться, что только один флажок установлен в 'true'
if (isYesFilter) {
$('#multibrand-no').prop('checked', false);
} else {
$('#multibrand-yes').prop('checked', false);
}
// Показать загрузочный экран
$('body').block({
message: 'Обновление продуктов...',
overlayCSS: { background: '#fff', opacity: 0.6 }
});
// Сделать AJAX-запрос для обновления продуктов
$.ajax({
url: wc_add_to_cart_params.ajax_url,
type: 'POST',
data: {
action: 'multibrand_compatible_products',
filter_yes: isYesFilter && isChecked,
filter_no: !isYesFilter && isChecked
},
success: function(response) {
if (response.success) {
// Обновить отображение продукции
$('.products').html(response.data.filtered_products);
if (response.data.other_products) {
$('.products').append('<h2>Другие продукты</h2>' + response.data.other_products);
}
}
$('body').unblock();
},
error: function() {
console.error('Не удалось обновить продукты');
$('body').unblock();
},
});
});
// Инициализировать состояние при загрузке страницы
$(document).ready(function() {
let params = new URLSearchParams(window.location.search);
let multibrandValue = params.get('multibrand');
// Сбросить флажки
$('#multibrand-yes, #multibrand-no').prop('checked', false);
// Установить начальное состояние в зависимости от URL параметра
if (multibrandValue === 'yes') {
$('#multibrand-yes').prop('checked', true);
} else if (multibrandValue === 'no') {
$('#multibrand-no').prop('checked', true);
}
// Активировать начальный фильтр, если нужно
if (multibrandValue) {
$('#multibrand-yes, #multibrand-no').first(':checked').trigger('change');
}
});
});
</script>
<?php
// Вернуть захваченный HTML-вывод
return ob_get_clean();
}
// Зарегистрировать AJAX-действия как для вошедших, так и для невошедших пользователей
add_action('wp_ajax_multibrand_compatible_products', 'multibrand_compatible_products');
add_action('wp_ajax_nopriv_multibrand_compatible_products', 'multibrand_compatible_products');
// AJAX-обработчик для фильтрации продуктов
function multibrand_compatible_products() {
// Проверка входящих данных
if (!isset($_POST['filter_yes']) || !isset($_POST['filter_no'])) {
wp_send_json_error();
return;
}
$filter_yes = $_POST['filter_yes'] === 'true';
$filter_no = $_POST['filter_no'] === 'true';
// Включить универсальную функцию фильтрации
require_once get_template_directory() . '/functions.php_FILES/Filter/unified_filter.php';
// Применить фильтр и получить результаты
$result = unified_product_filter('pa_multibrand-compatible', $filter_yes, $filter_no);
// Вернуть отфильтрованные результаты в виде JSON-ответа
wp_send_json_success($result);
}
Я создал универсальную систему запросов для остальной части моего кода, чтобы упростить масштабирование:
<?php
function unified_product_filter($attribute, $filter_yes, $filter_no) {
// Настроить базовые аргументы для WP_Query для получения продуктов
$filtered_args = array(
'post_type' => 'product',
'posts_per_page' => -1, // Получить все соответствующие продукты
);
// Инициализировать массив tax_query для фильтрации по атрибутам продукта
$filtered_args['tax_query'] = array(
'relation' => 'AND', // Все условия в этом массиве должны быть выполнены
);
// Добавить фильтры атрибутов в зависимости от выбора пользователя
if ($filter_yes) {
// Если выбрано 'Да', добавить фильтр для продуктов с значением 'yes' для данного атрибута
$filtered_args['tax_query'][] = array(
'taxonomy' => $attribute, // Атрибут продукта для фильтрации (например, 'pa_multibrand-compatible')
'field' => 'slug',
'terms' => 'yes',
);
} elseif ($filter_no) {
// Если выбрано 'Нет', добавить фильтр для продуктов с значением 'no' для данного атрибута
$filtered_args['tax_query'][] = array(
'taxonomy' => $attribute,
'field' => 'slug',
'terms' => 'no',
);
}
// Выполнить запрос для получения отфильтрованных продуктов
$filtered_query = new WP_Query($filtered_args);
// Инициализировать переменные для хранения HTML-вывода
$filtered_products_html="";
$other_products_html="";
// Проверить, есть ли отфильтрованные продукты
if ($filtered_query->have_posts()) {
while ($filtered_query->have_posts()) {
$filtered_query->the_post();
// Генерировать HTML для каждого отфильтрованного продукта
$filtered_products_html .= generate_product_html(get_the_ID());
}
wp_reset_postdata(); // Сбросить глобальную переменную $post
}
// Если нет продуктов, соответствующих фильтру, получить все продукты
if (empty($filtered_products_html)) {
$all_products_query = new WP_Query(array(
'post_type' => 'product',
'posts_per_page' => -1,
));
if ($all_products_query->have_posts()) {
while ($all_products_query->have_posts()) {
$all_products_query->the_post();
// Генерировать HTML для каждого продукта
$other_products_html .= generate_product_html(get_the_ID());
}
wp_reset_postdata();
}
}
// Вернуть результаты в виде массива
return array(
'filtered_products' => $filtered_products_html,
'other_products' => $other_products_html,
);
}
// Вспомогательная функция для генерации HTML для одного продукта
function generate_product_html($product_id) {
$product = wc_get_product($product_id);
// Генерировать HTML для продукта (настраивайте по мере необходимости)
$html="<div class="product">";
$html .= '<h3>' . $product->get_name() . '</h3>';
$html .= '<p>Цена: ' . $product->get_price_html() . '</p>';
// Добавьте больше деталей о продукте по необходимости
$html .= '</div>';
return $html;
}
Я считаю, что каждый раз, когда добавляется новый фильтр, будь то через нажатие на флажок или выбор из выпадающего списка, запрос на загрузке либо перезаписывает все остальное, либо делает запрос только для последнего переключаемого фильтра. Есть ли способ, чтобы фильтрация работала при загрузке страницы для каждого отдельного URL-адреса фильтрованного атрибута? (например: tilt=yes и multibrand=no отображают продукты с наклоном «да» и совместимостью с несколькими брендами «нет»)
Ответ или решение
Благодарю за предоставленную информацию о проблеме с фильтрацией продуктов на вашем веб-сайте. Давайте разберемся подробно, как можно решить вашу ситуацию с фильтрацией по нескольким критериям, чтобы при загрузке страницы учитывались значения из URL, а также сделаем так, чтобы ваша логика фильтрации работала правильно при выборах пользователями.
1. Текущая Проблема
Ваша текущая система фильтрации не соответствует ожидаемому поведению. Когда вы выбираете фильтры "Tilt" и "Multibrand Compatible", результат выборки не соответствует комбинации фильтров, указанной в URL, например, /?multibrand=no&tilt=yes
. Это связано с тем, что ваша логика фильтрации применяется только к последнему выбранному фильтру, и предыдущий фильтр не учитывается.
2. Рекомендуемое Решение
Для корректной работы вы должны объединить фильтры и обрабатывать их одновременно. Это достигается путем изменения вашего AJAX-запроса и процесса обработки на стороне сервера.
Шаг 1: Модификация JavaScript
Вам нужно обновить ваш JavaScript для того, чтобы он отправлял информацию о состоянии всех чекбоксов при каждом изменении. Вот пример, как это можно реализовать:
// Измените обработчик событий чекбоксов
$('#multibrand-yes, #multibrand-no, #tilt-yes, #tilt-no').on('change', function() {
const params = new URLSearchParams(window.location.search);
// Обновите параметры на основе состояния чекбоксов
params.delete('multibrand');
params.delete('tilt');
if ($('#multibrand-yes').is(':checked')) {
params.set('multibrand', 'yes');
} else if ($('#multibrand-no').is(':checked')) {
params.set('multibrand', 'no');
}
if ($('#tilt-yes').is(':checked')) {
params.set('tilt', 'yes');
} else if ($('#tilt-no').is(':checked')) {
params.set('tilt', 'no');
}
// Обновите URL
history.pushState(null, '', '?' + params.toString());
// Добавьте код для отображения загрузки и AJAX-запроса здесь
// ...
});
Шаг 2: Модификация AJAX-обработчика
Теперь вам необходимо изменить ваш сервера на стороне AJAX для обработки новых параметров. Поправьте код, чтобы он учитывал оба фильтра:
function multibrand_compatible_products() {
// Извлечение параметров фильтрации
$filter_yes_multibrand = isset($_POST['filter_yes']) && $_POST['filter_yes'] === 'true';
$filter_no_multibrand = isset($_POST['filter_no']) && $_POST['filter_no'] === 'true';
$filter_yes_tilt = isset($_POST['tilt_yes']) && $_POST['tilt_yes'] === 'true';
$filter_no_tilt = isset($_POST['tilt_no']) && $_POST['tilt_no'] === 'true';
// Применение фильтров
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
'tax_query' => array('relation' => 'AND'),
);
// Добавление фильтров Multibrand
if ($filter_yes_multibrand) {
$args['tax_query'][] = array(
'taxonomy' => 'pa_multibrand-compatible',
'field' => 'slug',
'terms' => 'yes',
);
} elseif ($filter_no_multibrand) {
$args['tax_query'][] = array(
'taxonomy' => 'pa_multibrand-compatible',
'field' => 'slug',
'terms' => 'no',
);
}
// Добавление фильтров Tilt
if ($filter_yes_tilt) {
$args['tax_query'][] = array(
'taxonomy' => 'pa_tilt',
'field' => 'slug',
'terms' => 'yes',
);
} elseif ($filter_no_tilt) {
$args['tax_query'][] = array(
'taxonomy' => 'pa_tilt',
'field' => 'slug',
'terms' => 'no',
);
}
// Выполнение запроса
$filtered_query = new WP_Query($args);
// Дальнейшая обработка и возвращение результатов
// ...
}
3. Обновление страницы
Кроме того, вам нужно будет убедиться, что при загрузке страницы изначально применяются все параметры, переданные из URL. Вы можете добавить соответствующий код в $(document).ready()
:
$(document).ready(function() {
let params = new URLSearchParams(window.location.search);
// Установите начальные состояния всех чекбоксов
if (params.get('multibrand') === 'yes') {
$('#multibrand-yes').prop('checked', true);
} else if (params.get('multibrand') === 'no') {
$('#multibrand-no').prop('checked', true);
}
if (params.get('tilt') === 'yes') {
$('#tilt-yes').prop('checked', true);
} else if (params.get('tilt') === 'no') {
$('#tilt-no').prop('checked', true);
}
// Инициировать первую фильтрацию, если параметры заданы
if (params.get('multibrand') || params.get('tilt')) {
$('#multibrand-yes, #multibrand-no, #tilt-yes, #tilt-no').trigger('change');
}
});
Заключение
В итоге, чтобы гарантировать правильную фильтрацию продуктов по нескольким критериям, необходимо внести изменения в JavaScript и на сервере. Убедитесь, что все фильтры обрабатываются вместе, и только соответствующие продукты отображаются согласно выбранным вариантам. Это не только улучшит пользовательский опыт, но и обеспечит точные результаты на сайте, что, в свою очередь, повысит удовлетворенность клиентов и вероятность конверсии.
Эти изменения обеспечат более гибкую и эффективную систему фильтрации, соответствующую современным требованиям пользователей.