Добавление на сайт AJAX фильтра записей (типов постов) по рубрикам (таксономиям), произвольным полям либо по дате. Сортировка по убыванию / возрастанию.

В последнее время замечаю, что всё больше постов публикуются по просьбам читателей, так что пишите, возможно и для вас разберу какую-нибудь тему 🙂

Сейчас я покажу вам, как легко и просто создавать асинхронные фильтры записей/товаров (и те и вторые — это произвольные типы постов) на сайте при помощи всего лишь jQuery и WP_Query.

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

1. HTML форма фильтра

Форма будет состоять из выпадающего списка таксономий (для простоты — рубрик), диапазона значений произвольного поля (например пусть это будет цена), радио-кнопок сортировки по дате, по убыванию и возрастанию, а также чекбокса, который позволит отфильтровать посты, отобразив только те из них, у которых имеется миниатюра.

Таксономии

Неважно, элементы какой таксономии вы хотите вывести, в любых случаях функция get_terms() мне кажется ну самым удобным вариантом.

Для того, чтобы вывести элемент выпадающего списка с её помощью, вы можете воспользоваться следующим кодом:

if( $terms = get_terms( 'category', 'orderby=name' ) ) : // как я уже говорил, для простоты возьму рубрики category, но get_terms() позволяет работать с любой таксономией
	echo '<select name="categoryfilter"><option>Выберите категорию...</option>';
	foreach ($terms as $term) :
		echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // в качестве value я взял ID рубрики
	endforeach;
	echo '</select>';
endif;

Произвольные поля

Для примера возьмём минимальную и максимальную цену.

Ну тут всё легко, можно просто использовать два HTML-инпута. Конечно, будет гораздо интереснее элемент range с возможностью передвигания ползунков минимального и максимального значения. Но это уже на ваше усмотрение, плагинов jQuery много, что-нибудь да выберете.

<input type="text" name="cena_min" placeholder="Минимальная цена" />
<input type="text" name="cena_max" placeholder="Максимальная цена" />

Дата

Как я уже писал выше — это будут две radio-кнопки.

<label><input type="radio" name="date" value="ASC" /> Дата: по возрастанию</label>
<label><input type="radio" name="date" value="DESC" selected="selected" /> Дата: по убыванию</label>

Чекбокс «Только с фото»

Ну тут вообще всё просто.

<label><input type="checkbox" name="featured_image" /> Только с миниатюрой</label>

Форма целиком

Так как данная статья относится к разделу для начинающих, то не поленюсь теперь подвести итог и скинуть весь код получившейся формы.

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">
<?php
if( $terms = get_terms( 'category', 'orderby=name' ) ) : // как я уже говорил, для простоты возьму рубрики category, но get_terms() позволяет работать с любой таксономией
	echo '<select name="categoryfilter"><option>Выберите категорию...</option>'
	foreach ($terms as $term) :
		echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // в качестве value я взял ID рубрики
	endforeach;
	echo '</select>';
endif;
?>
<input type="text" name="cena_min" placeholder="Минимальная цена" />
<input type="text" name="cena_max" placeholder="Максимальная цена" />
<label><input type="radio" name="date" value="ASC" /> Дата: по возрастанию</label>
<label><input type="radio" name="date" value="DESC" selected="selected" /> Дата: по убыванию</label>
<label><input type="checkbox" name="featured_image" /> Только с миниатюрой</label>
<button>Применить фильтр</button>
<input type="hidden" name="action" value="myfilter">
</form>
<div id="response"></div>

2. jQuery-скрипт для отправки запроса и получения данных

Тут я подразумеваю, что вы уже знаете, что такое jQuery и умеете его как минимум подключать на сайт.

jQuery(function($){
	$('#filter').submit(function(){
		var filter = $(this);
		$.ajax({
			url:ajaxurl, // обработчик
			data:filter.serialize(), // данные
			type:filter.attr('method'), // тип запроса
			beforeSend:function(xhr){
				filter.find('button').text('Загружаю...'); // изменяем текст кнопки
			},
			success:function(data){
				filter.find('button').text('Применить фильтр'); // возвращаеи текст кнопки
				$('#response').html(data);
			}
		});
		return false;
	});
});

3. Обработчик PHP

Ну и последний шаг. Основная задача, с которой у многих людей возникают трудности — это задание аргументов для WP_Query с учётом всех параметров фильтра выше.

function true_filter_function(){
	$args = array(
		'orderby' => 'date', // сортировка по дате у нас будет в любом случае (но вы можете изменить/доработать это)
		'order'	=> $_POST['date'] // ASC или DESC
	);
 
	// для таксономий
	if( isset( $_POST['categoryfilter'] )
		$args['tax_query'] = array(
			array(
				'taxonomy' => 'category',
				'field' => 'id',
				'terms' => $_POST['categoryfilter']
			)
		);
 
	// создаём массив $args['meta_query'] если указана хотя бы одна цена или отмечен чекбокс
	if( isset( $_POST['cena_min'] ) || isset( $_POST['cena_max'] ) || ( isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' ) )
		$args['meta_query'] = array( 'relation'=>'AND' ); // AND значит все условия meta_query должны выполняться
 
	// условие 1: цена больше $_POST['cena_min']
	if( isset( $_POST['cena_min'] ) )
		$args['meta_query'][] = array(
			'key' => 'cena',
			'value' => $_POST['cena_min'],
			'type' => 'numeric',
			'compare' => '>'
		);
 
	// условие 2: цена меньше $_POST['cena_max']
	if( isset( $_POST['cena_max'] ) )
		$args['meta_query'][] = array(
			'key' => 'cena',
			'value' => $_POST['cena_max'],
			'type' => 'numeric',
			'compare' => '<'
		);
 
	// условие 3: миниатюра имеется
	if( isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
		$args['meta_query'][] = array(
			'key' => '_thumbnail_id',
			'compare' => 'EXISTS'
		);
 
	die();
}
 
 
add_action('wp_ajax_myfilter', 'true_filter_function'); 
add_action('wp_ajax_nopriv_myfilter', 'true_filter_function');

Если особый интерес у вас вызывают последние две строчки, то тема AJAX в WordPress подробно раскрыта тут.

Миша

В последние годы я долго не знал, что мне делать с сайтом misha.blog, ведь он практически не приносит никакого профита, но недавно я осознал, что моя миссия – способствовать распространению WordPress. Ведь WordPress – это лучший движок для разработки сайтов – как для тех, кто готов использовать заложенную структуру этой CMS, так и для тех, кто предпочитает headless решения.

Сам же я впервые познакомился с WordPress в 2009 году. Организатор WordCamp. Преподаватель в школах Epic Skills и LoftSchool.

Если вам нужна помощь с вашим сайтом или может даже разработка с нуля на WordPress / WooCommerce — пишите. Я и моя команда сделаем вам всё на лучшем уровне.

Комментарии — 17

Комментирование этого поста более не доступно.
  • Дмитрий 2 марта 2017 #

    В данном случае куда вставлять код обработчика php?
    Обычно же для подключения обработчика нужен скрипт, ктрй его вызывает, а сам обработчик в отдельном файле.

    • Миша 3 марта 2017 #

      Обработчик отправляйте в functions.php, Дмитрий. Более подробно об этом.

  • eveniy 22 марта 2017 #

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

    • Миша 23 марта 2017 #

      Добрый день!

      В принципе вам только немного нужно доработать код из этого поста - категории уже есть, цена - это и так уже произвольное поле.

      Вот этот кусочек кода нужно задействовать для ваших трёх произвольных полей:

      if( isset( $_POST['pole1_name'] ) )
      		$args['meta_query'][] = array(
      			'key' => 'pole1',
      			'value' => $_POST['pole1_name']
      		);

      То есть я убрал из этого массива параметр сравнения и параметр числового значения. Дайте знать, если будут вопросы.

  • Noomad 16 апреля 2017 #

    Привет Михаил!

    У меня такой вопрос, вроде сделал по инструкции:

    -добавил форму на страницу
    -добавил script jquery
    -добавил обработчик в functions.php

    при нажатии на кнопку меня перебрасывает на страницу
    testsite/wp-admin/admin-ajax.php

    что не так? подскажите пожалуйста. Ведь ajax на то и ajax чтоб подгрузить данные на текущей странице, верно?

    • Миша 16 апреля 2017 #

      Привет,

      верно,

      такое происходит при наличии ошибок в JavaScript, прежде всего рекомендую открыть консоль браузера и посмотреть, что там интересного. Обычно там уже сразу написано, где именно находится проблема.

      • Noomad 16 апреля 2017 #

        Консоль открыл, ошибок не показывает.

        я еще приписал в скрипт строчку (в скрипт jquery):
        console.log('TEST');

        тест выводится и ошибок ни каких, при нажатии на кнопку переводит на страницу
        testsite/wp-admin/admin-ajax.php

        у меня тогда такой вопрос, а почему оно не должно переводить ? Ведь по сути у нас в форме указан наш admin-ajax.php.

        А в самом скрипте jquery я не вижу чтоб поведение кнопки блокировалось, поэтому по логике вроде и должно перебросить. Или я не прав?

        Очень хочу научиться делать фильтры).

        • Миша 17 апреля 2017 #

          И правда, не блокируется 🙂 ну так добавьте, неужели не рискнули попробовать? Я просто забыл туда дописать return false;

          • Александра 13 ноября 2019 #

            Добрый вечер, у меня такая же проблема, я так и не поняла как это исправить, где именно надо прописать return false;

  • noff_e 16 декабря 2019 #

    Миша, добрый день. Как делать фильтры, я понял. Но как теперь мне вывести отфильтрованные товары стандартным loop'ом на страницу с помощью AJAX? Придётся писать свой шаблон для сетки товаров или есть стандартная функция вывода всех отфильтрованных товаров? По типу такого:

            $q = new WP_Query($args);
        if($q->have_posts()) {
            /*
             * затем запускаем цикл
             */
            while($q->have_posts()){
                $q->the_post();
                /*
                 * выводим например ссылку на каждый пост
                 */
                echo фукнция_вывода_поста_как_в_стандартном_шаблоне( $post );
            }
        }
        /*
         * восстанавливаем глобальную переменную $post
         */
        wp_reset_postdata();

    Т.е. функция по факту должна возвращать html код сетки товаров.

    • noff_e 17 декабря 2019 #

      Нашёл решение:

      $loop = new WP_Query( $args );
        if ( $loop->have_posts() ) {
            while ( $loop->have_posts() ) : $loop->the_post();
                wc_get_template_part( 'content', 'product' );
            endwhile;
        } else {
            echo __( 'No products found' );
        }
        wp_reset_postdata();
  • Заур 31 января 2020 #

    Здравствуйте! Скажите, а как добавить постраничную навигацию - loadmore ajax к отфильтрованным постам? То есть отфильтровали по значению, а далее если их много подгружаем аяксом по кнопке "Показать еще"...

    • Миша 2 февраля 2020 #

      Здравствуйте Заур,

      У меня определённо было подробный урок на эту тему, поищите пожалуйста 🙃 Если не ошибаюсь, он на английском языке.

  • Эдуард 27 марта 2020 #

    Такое впечатление, что автор скопировал код со стороннего сайта, совершенно не понимая, как он работает.
    В приведенном виде, код не будет работать никогда.
    В jQuery скрипте не указан адрес по которому отсылается запрос, вместо него стоит не инициализированная переменная ajaxurl.
    Передача функции обработчика вообще отсутствует.
    В форме зачем-то задано скрытое поле, которое в остальном коде никак не используется...
    Копипаст вещь, конечно, хорошая, но вы же делаете вид, что людей учите чему-то. А на самом деле сами не понимает, что пишите.

    • Миша 27 марта 2020 #

      Ахаха, спасибо, посмеялся.

      Зачем же так расстраиваться, Эдуард, если у вас ничего не получается 🙃 Да, урок не самый подробный, ну это уже мой выбор, могли бы просто задать вопрос, если вам что-то непонятно.

      Например: "Добрый день, Миша! Я не могу понять, откуда берётся переменная ajaxurl, помогите пожалуйста." А вместо этого вы оставили после себя 💩 в комментариях.

      • Эдуард 27 марта 2020 #

        Ну ОК, раз пошла такая пьянка, действительно, расскажите, откуда у вас берется переменная ajaxurl на фронт-энде.
        Мы же здесь про фронт-энд говорим правда? Вряд ли вы здесь про поиск и фильтрацию в админке пишите. По крайней мере никаких замечаний по этому поводу у вас нет.
        Заодно расскажите, зачем вы в ajax форме неиспользуемый вами атрибут action поставили, из-за чего собственно людей и перекидывает на адрес /wp-admin/admin-ajax.php, затрудняя отладку.

        • Миша 27 марта 2020 #

          Скорее всего из admin_url( 'admin-ajax.php' ).

          Вероятно я предполагал в JS использовать filter.attr('action'), но забыл поменять.