Попытка реализовать функционал полноценного фильтра для различных ситуаций: по меткам, по категориям, по таксономиям, по дополнительным полям, по сочетанию всех этих данных.
Принцип работы фильтра
Создаем в шаблоне архива (категории, поиска, архива, таксономии) форму с параметрами фильтра. Нажимаем кнопку фильтровать, отправляя GET-запрос, и изменяем основной запрос WP.
Пример фильтра
Вставляем код фильтра в сайтбар (самый распространенный случай)
<div id="filter">
<form class="filter" action="" method="get">
<div><span class="name-group">Страна (метаполе)</span>
<span class="group-filter">
<label><select name="country" id="country">
<option value=""></option>
<option value="usa" <?php if('usa' == $_GET['country']): ?> selected="selected"<?php endif; ?>>usa</option>
<option value="functions">functions</option>
<option value="woocommerce">woocommerce</option>
<option value="video">video</option>
</select></label>
</span>
</div>
<!-- Фильтр по меткам -->
<div>
<span class="name-group">Метка</span>
<span class="group-filter">
<?php
$getcat = get_the_category();
$cat_id = $getcat[0]->term_id;
$args = array( 'posts_per_page' => -1, 'cat' => $cat_id );
$catposts = get_posts( $args );
foreach( $catposts as $post ){ setup_postdata($post);
$all_tag_objects = get_the_tags();
if($all_tag_objects){
foreach($all_tag_objects as $tag) {
if($tag->count > 0) { $all_tag_ids[] = $tag->term_id; }
}
}
}
wp_reset_postdata();
if ( $tag->count > 0 ): ?>
<?php $tag_ids_unique = array_unique($all_tag_ids); ?>
<?php foreach($tag_ids_unique as $tag_id): ?>
<?php
$post_tag = get_term( $tag_id, 'post_tag' );
//$rooms = implode(',' , $_GET['tags']);
?>
<label>
<input type="checkbox" name="tags"
value="<?php echo $post_tag->term_id; ?>"
<?php if($post_tag->term_id == $_GET['tags']): ?> checked<?php endif; ?>>
<span><?php echo $post_tag->slug; ?>
<sup>
<?php
$args = array(
'tag' => $post_tag->slug,
'cat' => $cat_id,
'posts_per_page' => -1);
$posts = get_posts($args);
$number = 0;
foreach ($posts as $post) :
$number++;
endforeach;
echo $number;
wp_reset_query(); ?>
</sup></span>
</label>
<?php endforeach; ?>
<?php endif; ?>
</span>
</div>
<div><span class="name-group">Лэйбл</span>
<span class="group-filter">
<?php if( $_GET['term'] != '' ) { echo '<input type="hidden" name="taxonomy" value="label"/>'; } ?>
<?php
$getcat = get_the_category();
$cat_id = $getcat[0]->term_id;
$args = array( 'posts_per_page' => -1, 'cat' => $cat_id );
$catposts = get_posts( $args );
foreach( $catposts as $post ){ setup_postdata($post);
$all_term_objects = get_terms( array(
'taxonomy' => 'label'
//'hide_empty' => false
) );
if($all_term_objects){
foreach($all_term_objects as $term) {
if($term->count > 0) { $all_term_ids[] = $term->term_id; }
}
}
}
wp_reset_postdata();
if ( $term->count > 0 ): ?>
<?php $term_ids_unique = array_unique($all_term_ids); ?>
<?php foreach($term_ids_unique as $term_id): ?>
<?php
$post_term = get_term( $term_id, 'label' );
?>
<label>
<input type="checkbox" name="term" value="<?php echo $post_term->slug; ?>"/>
<span><?php echo $post_term->slug; ?> <sup><?php echo $post_term->count; ?></sup></span>
</label>
<?php endforeach; ?>
<?php endif; ?>
</span>
</div>
<button type="submit"><i class="fa fa-filter" aria-hidden="true"></i>Фильтровать</button>
</form>
<form class="clear" action="" method="get">
<button action="<?php unset($_GET); ?>"><i class="fa fa-refresh" aria-hidden="true"></i>Отчистить</button>
</form>
</div>
Подробнее о некоторых моментах
Проверка, если есть такой тэг в запросе, ставить checked:
<input type="checkbox" name="tags" value="<?php echo $post_tag->term_id; ?>" <?php if($post_tag->term_id == $_GET['tags']): ?> checked<?php endif; ?>> <span><?php echo $post_tag->slug; ?> <sup>
CSS Фильтра
#filter {
background-color: #111;
color: #fff;
margin-bottom: 30px;
padding: 15px;
}
#filter div {
float: left;
width: 100%;
padding: 15px 0;
border-top: 1px solid #222;
border-bottom: 1px solid #000;
}
#filter div:first-child {border-top: none;}
#filter div:last-child {border-bottom: none;}
.name-group {
width: 100%;
float: left;
padding: 2px 0;
margin-bottom: 10px;
color: #7cc30b;
position: relative;
}
.name-group:before {
content: "-";
position: absolute;
right: 0;
font-size: 16px;
color: #fff;
}
.name-group-close:before {content: "+" !important;}
.group-filter {
width: 100%;
float: left;
}
#filter label {
float: left;
width: 100%;
margin-bottom: 5px;
display: table;
}
#filter label input {
display: table-cell;
vertical-align: middle;
}
#filter label span {
display: table-cell;
vertical-align: middle;
width: 90%;
}
#filter label span sub {color: #777;}
#filter select {width: 100%;}
#filter button {
background-color: #7cc30b;
padding: 5px 10px;
width: 100%;
border: none;
font-weight: bold;
cursor: pointer;
border-radius: 3px;
}
#filter button:hover {background-color: #9aef15;}
#filter .fa {color: #fff;}
form.clear {margin-top: 15px;}
.clear button {background-color: #777 !important;}
.clear button:hover {background-color: #555 !important;}
Немного jQuery
//Скрипты фильтра
$(document).ready(function() {
// Проверка, чтоб не отправлялись пустые значения
$("#filter").submit(function() {
if($("#country").val()=="") { $("#country").prop("disabled",true); }
if($("#component").val()=="") { $("#component").prop("disabled",true); }
});
// Раскрывающийся блок
$('.name-group').click(function(){
$(this).next().slideToggle();
$(this).toggleClass('name-group-close');
});
});
Обработчик фильтра
Добавляем данную функцию, либо в специальный файл php и вызываем его в данный шаблон, либо вставляем ее в файл functions.php
/* Фильтрация */
function go_filter() { // наша функция
//$args = array(); // подготовим массив
$args['meta_query'] = array('relation' => 'AND'); // отношение между условиями, у нас это "И то И это", можно ИЛИ(OR)
//$args['tax_query'] = array('relation' => 'AND'); // отношение между условиями, у нас это "И то И это", можно ИЛИ(OR)
global $wp_query; // нужно заглобалить текущую выборку постов
// Выводим параметры запроса, чтобы было наглядно по каким параметрам фильтруем
echo $_GET['country'];
echo $_GET['component'];
print_r ($_GET['tags']);
echo $_GET['term'];
if ($_GET['country'] != '') { // если передана фильтрация по разделу
$args['meta_query'][] = array( // пешем условия в meta_query
'key' => 'country', // название произвольного поля
'value' => $_GET['country'] // переданное значение произвольного поля
);
}
if( $_GET['tags'] != '' ) {
$args['tag__and'] = $_GET['tags'];
}
if( $_GET['term'] != '' ) {
$args['tax_query'][] = array(
'taxonomy' => 'label',
'field' => 'id',
'terms' => array($_GET['term'])
);
}
if ($_GET['price_ot'] != '' || $_GET['price_do'] != '') { // если передано поле "Цена от" или "Цена до"
if ($_GET['price_ot'] == '') $_GET['price_ot'] = 0; // если "Цена от" пустое, то значит от 0 и выше
if ($_GET['price_do'] == '') $_GET['price_do'] = 9999999; // если "Цена до" пустое, то будет до 9999999
$args['meta_query'][] = array( // пешем условия в meta_query
'key' => 'price', // название произвольного поля
'value' => array( (int)$_GET['price_ot'], (int)$_GET['price_do'] ), // переданные значения ОТ и ДО для интервала передаются в массиве
'type' => 'numeric', // тип поля - число
'compare' => 'BETWEEN' // тип сравнения, здесь это BETWEEN - т.е. между "Цены от" и до "Цены до"
);
}
if (!empty($_GET['rooms'])) { // если передан массив с фильтром по комнатам
$args['meta_query'][] = array( // пешем условия в meta_query
'key' => 'rooms', // название произвольного поля
'value' => $_GET['rooms'], // переданное значения, $_GET['rooms'] содержит массив со значениями отмеченных чекбоксов
'type' => 'numeric', // тип поля - число
'compare' => 'IN' // тип сравнения IN, т.е. значения поля комнат должно быть одним из значений элементов массива
);
}
if ($_GET['photo'] != '') { // если передано поле "Только с фото"
$args['meta_query'][] = array( // пешем условие в meta_query
'key' => '_thumbnail_id', // поле _thumbnail_id должно быть, это зарезервированное имя wp
);
}
if ($_GET['keyword'] != '') { // если передано поле "Ключевое слово"
$args['s'] = $_GET['keyword']; // пешем значение в ключ "s" условий выборки, обратите внимание это уже не произвольное поле для meta_query, будет работать как обычный поиск + остальные условия
}
query_posts(array_merge($args,$wp_query->query)); // сшиваем текущие условия выборки стандартного цикла wp с новым массивом переданным из формы и фильтруем
}
Когда мы меняем основной запрос, нужно изменяемый параметр указывать в квадратных скобках. Примеры: $args[‘meta_query’], $args[‘tag__and’], $args[‘tax_query’] и т.д.
Изменяем параметры основного запроса
Перед выводом записей выводим выбранные параметры фильтра
<?php
if ($_GET && !empty($_GET)) { // если было передано что-то из формы
go_filter(); // запускаем функцию фильтрации
}
?>
Убедитесь, что в шаблоне нет других фильтров, которые могут перебивать фильтрацию.
Проблемы при реализации
При выборке записей с определенным термином таксономии (из шаблона категории) выдает шаблон данной таксономии — это не правильно. Фильтр должен выдавать записи с этим термином.
Если сделать такой запрос в пользовательской странице, то результат будет правильный — записи с термином.
Возможно надо выводить результаты фильтра в специальном шаблоне?!
Полезные «вещи» при создании фильтра
Если нужно передать несколько значений поля (например чекбоксы), то в параметр name нужно добавлять квадратные скобки name=»tags[]».
При выводе подобного массива, мы должны у параметра убрать принадлежность к массиву — array( ) и выводить напрямую
Вместо записи
$args['tax_query'][] = array(
'taxonomy' => 'label',
'field' => 'slug',
'terms' => array($_GET['label']),
'operator' => 'AND'
);
Вот такую запись:
$args['tax_query'][] = array(
'taxonomy' => 'label',
'field' => 'slug',
'terms' => $_GET['label'],
'operator' => 'AND'
);
implode — превращает значения переменных массива в строку со значениями через запятую.
$exclude_tags = implode(',', $tag_ids_unique);
Тестирование и отладка фильтра
[Это не всегда работает] Определить и вывести количество записей в основном запросе. Можно ставить в нескольких местах проверяя как влияет код на количество записей в основном запросе.
<?php global $wp_query; echo $wp_query->found_posts; // Сколько записей в основном запросе ?>
Вывод всех значений GET запроса
<?php echo "<pre>"; print_r($_GET); echo "</pre>"; ?>
Фиксирование значений. При единичном значении (селект, радио):
<?php if('usa' == $_GET['country']): ?> selected="selected"<?php endif; ?>
или
<?php if($post_term->slug == $_GET['label']): ?> checked<?php endif; ?>
с множественными значениями чекбоксов надо проверять соответствие со значениями массива:
<?php if (in_array($post_tag->term_id, $_GET['tags'])) { echo'checked'; } ?>
Корректная кнопка очистки фильтра (используем java script и технологию window.location):
У ссылки должен быть пустой атрибут href=»» и id=filter-clear + добавляем скрипт
$(document).ready(function() {
var x = location.origin + location.pathname;
$('#filter-clear').attr('href', x);
});
Что нужно еще доработать
1. При фильтрации из категории меняется ориентации где мы находимся: метка (принимает свойства шаблона tag.php), таксономия (перекидывает в шаблон taxonomy.php). Если сделать на отдельной странице это устраняется но там нет пэйджинации. При отключении (удалении) шаблона taxonomy.php фильтр по таксономиям работает нормально. !Происходит это видимо из-за изменения основного запроса. Эта же проблема препятствует нормальной выборке таксономии label, при указании 2-х разных терминов — выдает записи содержащих оба термина, хотя оператор указан — IN.
2. Динамический вывод таксономии. В данный момент я ее прописываю вручную.
3. Фильтр не работает если его применять не с первой страницы! — Думаю, нужно разработать пользовательскую пэйджинацию работающую в той же системе. Либо делать какой-то сброс пэйджинации при использовании фильтра.
4. Тестовые записи: Заглушка для сайта (functions + HIT), Фильтр по меткам (functions + ajax + usa), Дополнительные формы комментирования (NEW).
6. Вид url, сейчас из-за [] получается не красиво. По данному моменту задал вопрос на сайте wp-kama. Вот ответ. И еще вопрос возможно связанный с темой.
Вписал (вручную) в строку браузера, такой вид тэгов tags=99,78 (2 тэга одновременно: functions и ajax). При этом вывод переменной $_GET[‘tags’] выдает строку (не массив) 99,78, и фильтр из-за этого не понимает. Нужно преобразовывать строку создавая из нее массив:
explode — превращает строчные значения переменных в массив, нужно указать разделить элементов.
$tags = explode(",", $_GET['tags']);
$args['tag__and'] = $tags;
Таким же образом меняем $_GET[‘tags’] на $tags в форме фильтра для проверки какие чекбоксы включить.
if (in_array($post_tag->term_id, $tags)) { echo'checked'; }
Надо сделать $tags глобальной переменной чтобы не вызывать 2 раза.
Дело остается за малым, как-то внедрить такие значения (tags=99,78) в строку браузера. Сделать это возможно средствами java script нужно продумать такой скрипт.
Изменение URL реализовано с помощью функнционала js URLSearchParams.
7. Связать воедино и функции сортировки, но при этом вызывать 2 формы в разных местах. Идея (не верная): создать 2 дублирующие формы, но с разными полями (в одной собственно фильтр в другой сортировка). Нужно сделать добавляющий запрос в url (через js) продумать структуру такого запроса с условиями: есть ли уже GET запрос (сделал) и есть ли в этом запросе параметр select, если есть — заменить. Это реализовано благодаря js URLSearchParams.
8. Перезагрузка значений в самом фильтре. Не нужно усекать параметры! Возможно необходимо продумать систему изменения количества результатов.
9. Назначения — параметры по которым мы фильтруем, можно их выносить отдельными плашками с возможностью отключения. Выводить не отдельно параметр, а группу, т.е. не ajax, video, а tags, label и т.д.
10. Альтернативные вариант фильтра на ajax.
Тэги: фильтр
[site-socialshare]
