Фильтр на базе pdoResources
Если стоит задача отфильтровать ресурсы (часто - события), у которых заданы время начала и завершения, тип и город, решение можете найти ниже.
Самое оптимальное решение по фильтру - использовать фильтр в pdoResources - &tvFilters. Для тестирования пока что не задаем tpl, а выведем &select=`id,pagetitle`
[[pdoResources?
&parents=`6`
&select=`id,pagetitle`
&depth=`0`
&showHidden=`1`
&tvFilters=`event_type==show,event_city==Киев,event_date_end>=2014-12-21`
&sortby=`event_date_end`
&sortdir=`ASC`
]]
Значения TV, которые прописаны в фильтре так же будут показаны.
По-умолчанию, формат даты, который записывается в таблицу БД из инпута типа date, будет иметь вид Y-m-d H:i:s, поэтому для сравнения передаем дату в том же формате.
Для того, чтобы передать в фильтр GET-параметры, используем кастомный сниппет с именем, например, "filter":
[[pdoResources?
&parents=`6`
&select=`id,pagetitle`
&depth=`0`
&showHidden=`1`
&tvFilters=`[[!filter]]`
&sortby=`event_date_end`
&sortdir=`ASC`
]]
$filter = array();
if(isset($_GET['city'])){
if($_GET['city'] !== ''){
$filter[] = 'event_city=='.$_GET['city'];
}
}
if(isset($_GET['type'])){
if($_GET['type'] !== ''){
$filter[] = 'event_type=='.$_GET['type'].'%';
}
}
$date = date('Y-m-d H:i:s'); //по-умолчанию, ищем актуальные относительно текущай даты
if(isset($_GET['date'])){
if($_GET['date'] !== ''){
//конфертируем формат даты, если он не Y-m-d а, например, d/m/Y
$date = DateTime::createFromFormat('d/m/Y', $_GET['date']);
$date = $date->format('Y-m-d H:i:s');
$filter[] = 'event_date<='.$date; //ищем события, которые уже начались относительно заданой даты
}
}
$filter[] = 'event_date_end>='.$date;
return implode(',',$filter);
Обратите внимание, ф-я implode(',',$filter) соединяет параметры через запятую, это значит, что поиск работает по условию "И". Если соединить через "||", поиск будет идти по условию "ИЛИ".
В строке $filter[] = 'event_type=='.$_GET['type'].'%'; использован метасимвол %, который указывает, что нужно искать в нескольких возмоных значениях, например найти "show" в значениях "show||conkurs".
Кастомные фильтры
В случае, когда нужно получить только список id актуальных событий (которые еще не завершились), отсортированых от завершающихся до будущих, используем сниппет:
[[!actualEvents? &parent=`6`]]
$datenow = date('Y-m-d H:i:s');
$parent = $modx->getOption('parent', $scriptProperties, false);
$events = $modx->getChildIds($parent,1,array('published' => 1, 'deleted' => 0));
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=> 29,'value:>=' => $datenow));
$query->select('contentid');
$query->sortby('value','ASC');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
}
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
return implode(',',$events);
Более сложный сниппет - это фильтр, принимающий несколько параметров.
Самым оптимальным по скорости и самим гибким решением будет использование "каскадов" выборки id ресурсов из таблицы со значениями TV параметров, по-очередно проверяя значения каждого TV. Конечно, можно написать условие выборки в одном запросе к БД, но тогда теряется гибкость, например, мы не сможем сортировать список id событий только по дате.
Суть такова:
1) Получаем список всех id событий (дочерние этого контейнера).
2) Ищем множество id среди полученного в п.1 списка, для которых выполняется условие между параметром и значениями первого тв.
3) Перезаписываем список id, ищем по второму тв. Снова перезаписываем список id.
4) В последнем запросе сортируем, лимитируем, так, как нам нужно.
По-скольку такой фильтр - сугубо индивидуальное решение, то много параметров заданы в самом сниппете.
В примере мы можем задать 3 условия поиска:
1) Дата, которая должна вписываться в промежуток длительности события
2) Город, где событие проходит
3) Тип события (их может быть несколько для одного события)
4) Показать те, что в архиве
[[!eventsFilter?
&parent=`6`
&date=`15-08-2014`
&dateformat=`d-m-Y`
]]
$date = date('Y-m-d H:i:s');
$datenow = $date;
$parent = $modx->getOption('parent', $scriptProperties, false);
$events = $modx->getChildIds($parent,1,array('published' => 1, 'deleted' => 0));
$dateformat = $modx->getOption('dateformat', $scriptProperties, 'd-m-Y');
$tv_date_start_id = 26;
$tv_date_end_id = 29;
$tv_city_id = 28;
$tv_type_id = 27;
//фильтр по городам (event_city)
if(isset($_GET['city'])){
if($_GET['city'] !== ''){
$city = $_GET['city'];
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=>$tv_city_id,'contentid:IN' => $events, 'value:LIKE' => '%'.$city.'%'));
$query->select('contentid');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
}
}
//фильтр по категориям (event_type)
if(isset($_GET['type'])){
if($_GET['type'] !== ''){
$type = $_GET['type'];
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=>$tv_type_id, 'value:LIKE' => '%'.$type.'%'));
$query->select('contentid');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
}
}
$filterArchive = false;
if(isset($_GET['archive'])){
if($_GET['archive'] == 1){
$filterArchive = true;
}
}
$filterBydate = false;
if(isset($_GET['date'])){
if($_GET['date'] !== '' && !$filterArchive){
$filterBydate = true;
}
}
//фильтр по дате
if($filterBydate){
$date = DateTime::createFromFormat($dateformat, $_GET['date']);
$date = $date->format('Y-m-d H:i:s');
//date_from
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=> $tv_date_start_id,'value:<=' => $date));
$query->select('contentid');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
//date to
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=> $tv_date_end_id,'contentid:IN' => $events,'value:>=' => $date));
$query->select('contentid');
$query->sortby('value','ASC');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
}elseif($filterArchive){
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=> $tv_date_end_id,'value:<' => $date));
$query->select('contentid');
$query->sortby('value','ASC');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
}else{
$query = $modx->newQuery('modTemplateVarResource', array('contentid:IN' => $events, 'tmplvarid'=> $tv_date_end_id,'value:>=' => $date));
$query->select('contentid');
$query->sortby('value','ASC');
if($query->prepare() && $query->stmt->execute()){
$res = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
$events = array();
foreach($res as $r){
$events[] = $r['contentid'];
}
}
}
return implode(',',$events);
Список id передаем в pdoResources, Обязательно нужно сбросить сортировку самим pdoResources (&sortby=``), т.к. список id у нас уже отсортирован.
Теоретически, используя такую схему, можно сделать фильтр любой сложности.
!!! Рекомендую делать вызов pdoResources прямо в сниппете, который будет принимать GET или POST параметры, т.е. весь фильтр сделать одним сниппетом. Этот метод значительно добавит гибкости нашему фильтру:
Еще один вариант реализации AJAX-фильтра с AJAX-пагинацией на базе pdoResources
[[!catalogFilter? &tpl=`tpl.itemTour`]]
$filter = array();
if($_GET['some_value_first']){
$filter[] = 'some_tv_name_first=='.$_GET['some_value_first'];
}
if($_GET['some_value_second']){
$filter[] = 'some_tv_name_second=='.$_GET['some_value_second'];
}
$offset = 0;
if($_GET['offset']){
$offset = $_GET['offset'];
}
//get count of found resources
$params_count = array(
'parents' => 2,
'limit' => 0,
'tpl' => '@INLINE ,',
'select' => 'id',
'includeTVs' => 'some_tv_name_first,some_tv_name_second',
'tvPrefix' => '',
'showHidden' => '1',
'tvFilters' => implode(',',$filter)
);
$count = $modx->runSnippet('pdoResources',$params_count);
$count = count(explode(',',$count))-1;
//we can use this placeholder in template
$modx->setPlaceholder('count',$count);
$limit = 10;
$params = array(
'parents' => 2,
'limit' => $limit,
'offset' => $offset,
'tpl' => $tpl,
'select' => 'id,pagetitle,introtext,content',
'includeTVs' => 'tv_image,some_tv_name_first,some_tv_name_second',
'tvPrefix' => '',
'showHidden' => '1',
'sortby' => 'pagetitle',
'tvFilters' => implode(',',$filter)
);
$more = $count - $offset - $limit;
$lim = $more > $limit ? $limit : $more;
//FOR SEO
/*
if($offset > $count){
header("Location: ".$modx->makeUrl(2,'','','full'));
}
if($more > 1){
$modx->regClientStartupHTMLBlock('<link rel="next" href="' . $modx->makeUrl(2,'',array('offset' => $offset+10),'full') . '"/>');
}
if($offset >= 10){
$modx->regClientStartupHTMLBlock('<link rel="prev" href="' . $modx->makeUrl(2,'',array('offset' => $offset-10),'full') . '"/>');
}
*/
//FOR SEO END
$button = '';
if($more > 0){
$button = '<a id="load-more" href="#" class="btn-lg btn-primary">Загрузить еще '.$lim.' из '.$more.'</a>';
}
return $modx->runSnippet('pdoResources',$params).$button;
Сниппет нужно разместить непосредственно в шаблоне со страницой фильтра, и на странице ресурса (с пустым шаблоном), вызываемого аяксом.
$('#serch-results').on('click','#load-more',function(e){
e.preventDefault();
var offset = $('#serch-results > li').length;
$.ajax({
url: $('#filter').attr('data-ajax'),
data: $('#filter').serialize()+'&offset='+offset
}).done(function(response){
$('#load-more').remove();
$('#serch-results').append(response);
});
});
В основной форме указываем атрибут data-ajax куда прописываем url ресурса для ajax.