Состав фильтра, о котором пойдет речь:
- pdoTools;
- Сниппет, формирующий данные и запускающий pdoTools;
- Набор скриптов на Jquery для сбора данных и отправки их сниппету;
- Настроенная форма для фильтра.
Для начала, необходимо установить pdoTools, если его нет. На момент создания статьи, актуальная версия pdoTools — 2.12.2-pl.
Далее разберем форму и какие селекторы за что отвечают:
- .ajax-form — селектор формы фильтра, из которой будут отправляться данные;
- .ajax-start — селектор кнопки «отправить» для сбора данных и отправки (осуществления фильтрации);
- .ajax-reset — селектор кнопки сброса фильтра;
- .ajax-count — селектор количества найденных ресурсов;
- .ajax-container — блок-обертка для всех результатов поиска;
- .ajax-item — блок-обертка для каждого отдельного результата.
Схематично это выглядит так:
В самих скриптах можно поменять селекторы на желаемые, если эти не нравятся. Также если используется бутстрап сетка то можно комбинировать классы, например, .row .ajax-container или .col-12 .ajax-item.
Собственно, сам файл со скриптами:
$(function() {
//MODx pdoResources Ajax Filter
//Filter Settings
var fadeSpeed = 200, // Fade Animation Speed
ajaxCountSelector = '.ajax-count', // CSS Selector of Items Counter
ajaxContainerSelector = '.ajax-container', // CSS Selector of Ajax Container
ajaxItemSelector = '.ajax-item', // CSS Selector of Ajax Item
ajaxFormSelector = '.ajax-form', // CSS Selector of Ajax Filter Form
ajaxFormButtonStart = '.ajax-start', // CSS Selector of Button Start Filtering
ajaxFormButtonReset = '.ajax-reset', // CSS Selector of Button Reset Ajax Form
sortDownText = 'По убыванию',
sortUpText = 'По возрастанию';
function ajaxCount() {
if($('.ajax-filter-count').length) {
var count = $('.ajax-filter-count').data('count');
$(ajaxCountSelector).text(count);
} else {
$(ajaxCountSelector).text($(ajaxItemSelector).length);
}
}ajaxCount();
function ajaxMainFunction() {
$.ajax({
data: $(ajaxFormSelector).serialize()
}).done(function(response) {
var $response = $(response);
$(ajaxContainerSelector).fadeOut(fadeSpeed);
setTimeout(function() {
$(ajaxContainerSelector).html($response.find(ajaxContainerSelector).html()).fadeIn(fadeSpeed);
ajaxCount();
}, fadeSpeed);
});
}
$(ajaxContainerSelector).on('click', '.ajax-more', function(e) {
e.preventDefault();
var offset = $(ajaxItemSelector).length;
$.ajax({
data: $(ajaxFormSelector).serialize()+'&offset='+offset
}).done(function(response) {
$('.ajax-more').remove();
var $response = $(response);
$response.find(ajaxItemSelector).hide();
$(ajaxContainerSelector).append($response.find(ajaxContainerSelector).html());
$(ajaxItemSelector).fadeIn();
});
})
$(ajaxFormButtonStart).click(function(e) {
e.preventDefault();
ajaxMainFunction();
})
$(ajaxFormButtonReset).click(function(e) {
e.preventDefault();
$(ajaxFormSelector).trigger('reset');
$('input[name=sortby]').val('pagetitle');
$('input[name=sortdir]').val('asc');
setTimeout(function() {
$('[data-sort-by]').data('sort-dir', 'asc').toggleClass('button-sort-asc').text(sortUpText);
}, fadeSpeed);
ajaxMainFunction();
ajaxCount();
})
$(''+ajaxFormSelector+' input').change(function() {
ajaxMainFunction();
})
$('[data-sort-by]').data('sort-dir', 'asc').click(function() {
var ths = $(this);
$('input[name=sortby]').val($(this).data('sort-by'));
$('input[name=sortdir]').val($(this).data('sort-dir'));
setTimeout(function() {
$('[data-sort-by]').not(this).toggleClass('button-sort-asc').text(sortUpText);
ths.data('sort-dir') == 'asc' ? ths.data('sort-dir', 'desc').text(sortDownText) : ths.data('sort-dir', 'asc').text(sortUpText);
$(this).toggleClass('button-sort-asc');
}, fadeSpeed);
ajaxMainFunction();
});
});
Далее нам необходимо создать сниппет с любым названием, пусть это будет ajaxFilter. Смысл его заключается в получении данных из фронта, декодировании из json и формировании SQL-запроса для параметра &where используемого pdoTools.
Смотрим сниппет:
<?php
//Filter Fields Settings
$filter = array();
//Radio, Select & Text Fields Type
if($_GET['city']) {
$filter['city'] = $_GET['city'];
}
if($_GET['production']) {
$filter['production'] = $_GET['production'];
}
/*
//Two Text Fields From To
if($_GET['area_from']) {
$filter[] = 'area>='.$_GET['area_from'];
}
if($_GET['area_to']) {
$filter[] = 'area<='.$_GET['area_to'];
}*/
/*
//Для чекбоксов
if($_GET['garage']) {
$filter['garage:IN'] = $_GET['garage'];
}
*/
//End Settings
//Sort
if($_GET['sortby']) {
$sortby = $_GET['sortby'];
} else {
$sortby = 'pagetitle';
}
if($_GET['sortdir']) {
$sortdir = $_GET['sortdir'];
} else {
$sortdir = 'asc';
}
//End Sort
/*
//Offset
$offset = 0;
if($_GET['offset']){
$offset = $_GET['offset'];
}
*/
if($filter) {
$where = $modx->toJSON(array($filter));
} else {
$where = '';
}
$params_count = array(
'parents' => $parents,
'limit' => 0,
'tpl' => '@INLINE ,',
'select' => 'id',
'includeTVs' => $fields,
'showHidden' => '1',
'where' => $where
);
$count = $modx->runSnippet('pdoResources',$params_count);
$count = count(explode(',',$count))-1;
$modx->setPlaceholder('count',$count);
$params = array(
'parents' => $parents,
'limit' => $limit,
'offset' => $offset,
'tpl' => $tpl,
'select' => 'id,pagetitle,introtext,content',
'includeTVs' => 1,
'includeTVList' => $fields,
'showHidden' => '1',
'sortby' => $sortby,
'sortdir' => $sortdir,
'where' => $where
);
$more = $count - $offset - $limit;
$lim = $more > $limit ? $limit : $more;
$button = '';
if($more > 0){
$button = '<div class="ajax-filter-count" data-count="'.$count.'"><a href="#" class="ajax-more">Загрузить еще '.$lim.' из '.$more.'</a></div>';
}
return $modx->runSnippet('pdoResources',$params).$button;
Пример вызова сниппета для первоначального вывода элементов и дальнейшего взаимодействия:
[[!ajaxFilter? &tpl=`tplCatItem` &limit=`10` &parents=`5` &fields=`image,area,floor,garage,price` ]]
Идея этого фильтра взята вот отсюда, но с некоторыми доработками, а именно, переделан принцип формирования данных практически для каждых типов ввода данных (Чекбоксы, выпадающие списки, текстовые поля, значения "от" и "до"). Понятия не имею почему них указано по-другому т.к. в том виде фильтр малоработоспособен, комментарии с поправками они активно удаляли, но тем не менее, мой вариант проверен и обкатан и все пашет!