Вопрос или проблема
Я пишу фильтр товаров и столкнулся с этой проблемой.
У меня есть 2 различных атрибута моего фильтра товаров: Наклон — в виде флажка с “Да” или “Нет”, и Совместимость с несколькими брендами — в виде флажка с “Да” или “Нет”. Если я нажимаю на Совместимость с несколькими брендами “Нет”, то отображаются товары без атрибута Совместимости с несколькими брендами.
Допустим, изначально я кликаю на Совместимость с несколькими брендами с атрибутом “Нет”. Если затем я нажимаю на Наклон с атрибутом “Да”, то должны отображаться продукты, которые имеют наклон и не совместимы с несколькими брендами.
Вместо этого я получаю все товары с наклоном, включая те, которые имеют положительную Совместимость с несколькими брендами.
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());
// Убедитесь, что только один флажок выбран одновременно
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) {
// Если выбрано 'Да', добавьте фильтр для продуктов с значением 'да' для данного атрибута
$filtered_args['tax_query'][] = array(
'taxonomy' => $attribute, // Атрибут продукта для фильтрации (например, 'pa_multibrand-compatible')
'field' => 'slug',
'terms' => 'yes',
);
} elseif ($filter_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 при наложении нескольких фильтров в вашем продукте, необходимо следовать нескольким важным шагам, которые позволяют динамически обновлять фильтры и извлекать соответствующие продукты на основе сайт-параметров. Ваша задача заключается не только в корректном формировании URL с актуальными параметрами, но и в правильном применении логики фильтрации, чтобы отображались именно те продукты, которые соответствуют выбранным критериям.
1. Структура URL и параметры фильтрации
Каждый фильтр должен обновлять параметры URL в соответствии с его состоянием. Если вы применяете два фильтра, то параметры URL должны включать оба, например:
/?multibrand=no&tilt=yes
2. Основная логика обновления
В вашем JavaScript-коде вы уже начали реализовывать логику для обновления URL при изменении состояния чекбоксов. Однако важно, чтобы обработчик событий корректно обновлял все параметры, учитывая текущие состояния других фильтров.
Пример обновления URL
Во-первых, при изменении любого из фильтров вы должны считывать текущее состояние всех чекбоксов и обновлять URL следующим образом:
$('#multibrand-yes, #multibrand-no, #tilt-yes, #tilt-no').on('change', function() {
let currentUrl = new URL(window.location.href);
let params = currentUrl.searchParams;
// Чтение состояния всех фильтров
const multibrandYes = $('#multibrand-yes').is(':checked') ? 'yes' : null;
const multibrandNo = $('#multibrand-no').is(':checked') ? 'no' : null;
const tiltYes = $('#tilt-yes').is(':checked') ? 'yes' : null;
const tiltNo = $('#tilt-no').is(':checked') ? 'no' : null;
// Обновление параметров
if (multibrandYes) params.set('multibrand', multibrandYes);
else if (multibrandNo) params.set('multibrand', multibrandNo);
else params.delete('multibrand');
if (tiltYes) params.set('tilt', tiltYes);
else if (tiltNo) params.set('tilt', tiltNo);
else params.delete('tilt');
// Обновление URL без перезагрузки
history.pushState(null, '', '?' + params.toString());
// Вызов AJAX для обновления продуктов
updateProducts(params);
});
3. Применение фильтров и AJAX-запрос
Когда URL обновляется, необходимо отправить AJAX-запрос с применяемыми фильтрами. В вашем случае вы должны передавать значения всех фильтров и обновлять отображение продуктов на странице:
function updateProducts(params) {
// Отправка AJAX-запроса
$.ajax({
url: wc_add_to_cart_params.ajax_url,
type: 'POST',
data: {
action: 'multibrand_compatible_products',
filter_yes: params.get('multibrand') === 'yes',
filter_no: params.get('multibrand') === 'no',
tilt_yes: params.get('tilt') === 'yes',
tilt_no: params.get('tilt') === 'no',
},
success: function(response) {
// Обновление отображения продуктов
if (response.success) {
$('.products').html(response.data.filtered_products);
}
},
error: function() {
console.error('Failed to update products');
},
});
}
4. Унифицированная функция фильтрации
Ваше серверное решение с функцией unified_product_filter
выглядит отлично, однако вам необходимо добавить поддержку для фильтра "Tilt". Вы можете расширить tax_query
, чтобы также учитывать этот новый фильтр:
function unified_product_filter($attribute, $filter_yes, $filter_no, $tilt_yes, $tilt_no) {
// Остальная часть кода...
// Добавление фильтров для Tilt
if ($tilt_yes) {
$filtered_args['tax_query'][] = array(
'taxonomy' => 'pa_tilt', // Убедитесь, что название таксономии верно
'field' => 'slug',
'terms' => 'yes',
);
} elseif ($tilt_no) {
$filtered_args['tax_query'][] = array(
'taxonomy' => 'pa_tilt',
'field' => 'slug',
'terms' => 'no',
);
}
// Выполнение запроса
// Остальная часть функции...
}
5. Инициализация состояния при загрузке страницы
На этапе загрузки страницы ваша логика должна извлекать значения из URL параметров и инициализировать соответствующее состояние фильтров. В вашем текущем коде это уже реализовано, но обязательно убедитесь, что вы корректно проверяете и устанавливаете состояния для всех фильтров.
Заключение
Следуя этим рекомендациям, вы сможете обеспечить синхронизацию между URL-параметрами и фильтрами, что позволит вашим пользователям легко управлять выбором продуктов и находить именно те товары, которые им нужны. Убедитесь, что ваши AJAX-запросы корректно обрабатывают все возможные комбинации фильтров, и протестируйте процесс, чтобы гарантировать его безошибочную работу.