/ Wordpress / Фильтр. Принцип работы

Фильтр. Принцип работы

HIT

07.11.2016

2920

Попытка реализовать функционал полноценного фильтра для различных ситуаций: по меткам, по категориям, по таксономиям, по дополнительным полям, по сочетанию всех этих данных.

Принцип работы фильтра

Создаем в шаблоне архива (категории, поиска, архива, таксономии) форму с параметрами фильтра. Нажимаем кнопку фильтровать, отправляя 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.

Тэги:

Поделиться в соц. сетях:

  • Похожие записи
  • Комментарии
  • Вложения
Фильтр записей на главной странице

Фильтр записей на главной странице

Задокументирую очередной вариант фильтра постов. Фильтр располагается в шаблоне главной страницы. Верстка в шаблоне Подключение ajax и включение функции фильтра (из плагина): Верстка формы фильтра Скрипт отправки значений формы Для Читать далее »

Фильтр по меткам (изменение основного запроса)

Фильтр по меткам (изменение основного запроса)

Реализуем фильтрацию записей по меткам путем изменения основного запроса на лету (ajax). Находим все теги записей входящих в категорию Данный код можно вставить в боковую колонку в виде виджета. <?php Читать далее »

/ /
Фильтры Woocommerce

Фильтры Woocommerce

В базовую комплектацию Woocommerce входит набор виджетов для фильтрации товаров. Но данные виджеты необходимо несколько доработать. WooCommerce Навигация по слоям В этом виджете необходимо задать атрибут товара по которому будет Читать далее »

/ /

Добавить комментарий

Пока нет комментариев. Будь первым!

Фильтр. Принцип работы
Кросспостинг в соц.сетях
Рекомендации для васКросспостинг в соц.сетяхOpttour.ru
Спасибо! Наш менеджер свяжется с Вами в течении 5 минут.