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