Начнём с простого вывода на странице обычного списка ресурсов. Обязательно добейтесь, чтобы нормально работала AJAX-пагинация, потому что своих механизмов работы с AJAX мы писать не будем, а будем использовать методы AJAX, которые есть в pdoPage.
<div id="pdopage">
<div class="rows row">
{'!pdoPage' | snippet : [
'ajaxMode' => 'default',
'parents' => 0,
'limit' => 3,
'includeTVs' => 'height,weight,speed_type,price',
'tvPrefix' => '',
'tpl' => '@INLINE
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<div class="caption">
<h3>{$pagetitle}</h3>
<p>Вес: {$weight}</p>
<p>Высота: {$height}</p>
<p>Тип: {$speed_type}</p>
</div>
</div>
</div>',
]}
</div>
{'page.nav' | placeholder}
</div>Для фильтров я буду использовать такую вёрстку. Но вёрстка тут особой роли не играет — главное указать правильные классы в JS-коде.
<form class="form-horizontal" id="filters" action="">
<div class="form-group">
<label class="col-sm-1 control-label">Вес</label>
<div class="col-sm-5">
<input type="number" name="weight" class="form-control" placeholder="47">
</div>
<label class="col-sm-1 control-label">Высота</label>
<div class="col-sm-5">
<input type="number" name="height" class="form-control" placeholder="120">
</div>
</div>
<div class="form-group">
<label class="col-sm-1 control-label">Тип</label>
<div class="col-sm-11">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default">
<input type="radio" name="speed_type" value="fast"> Быстрые
</label>
<label class="btn btn-default">
<input type="radio" name="speed_type" value="slow"> Медленные
</label>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-1 control-label">Цена</label>
<div class="col-sm-11">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default">
<input type="checkbox" name="price[]" value="0-20000"> до 20 000 руб.
</label>
<label class="btn btn-default">
<input type="checkbox" name="price[]" value="20000-50000"> 20 000—50 000 руб.
</label>
<label class="btn btn-default">
<input type="checkbox" name="price[]" value="50000-100000"> 50 000—100 000 руб.
</label>
<label class="btn btn-default">
<input type="checkbox" name="price[]" value="100000-"> от 100 000 руб.
</label>
</div>
</div>
</div>
</form>JS-код обработки фильтров выглядит как-то так:
$(document).on('change keyup', '#filters input', function(){
// Проверяем, что pdoPage подключён
if (typeof(pdoPage) == 'undefined') return;
// Собираем значения всех фильтров в единый массив
var fields = {};
$.each($('#filters').serializeArray(), function(){
// Если параметр не является массивом (чекбоксом), то все просто
if (this.name.indexOf('[]') <= 0) {
fields[this.name] = this.value;
} else {
// Для чекбоксов посложнее
var name = this.name.replace('[]','');
if (typeof(fields[name]) == 'undefined') {
fields[name] = [];
}
fields[name].push(this.value);
}
});
// И отправляем этот массив на сервер.
$.post(document.location.href, {
action: 'filter',
fields: fields,
// Параметр hash - обязательный (он содержит настройки pdoPage)
hash: pdoPage.configs.page.hash
}, function(data) {
// Просим pdoPage загрузить новый список ресурсов
var tmp = document.location.href.split('?');
pdoPage.keys.page = 0;
pdoPage.loadPage(tmp[0], pdoPage.configs.page);
});
});Уже сейчас при изменении значения фильтров список ресурсов будет обновляться. Теперь осталось «объяснить», как обрабатывать фильтры. Для этого создаём плагин на событие OnHandleRequest:
<?php
if ($modx->context->key == 'mgr' || empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') return;
switch ($_POST['action']) {
case 'filter':
$output = array('success' => false, 'message' => '');
// Проверяем, что hash получен и параметры pdoPage существуют
if (isset($_POST['hash']) && !empty($_POST['hash'])
&& isset($_SESSION['pdoPage'][$_POST['hash']])
&& !empty($_SESSION['pdoPage'][$_POST['hash']])) {
$hash = (string) $_POST['hash'];
// Указываем только ТВ, доступные для фильтрации
$tvs = array('height','weight','speed_type');
// Наполняем условие выборки
$where = array();
foreach ($tvs as $tv) {
switch ($tv) {
case 'price[]':
// Пример обработки чекбоксов
if (isset($_POST['fields']['price']) && !empty($_POST['fields']['price'])) {
$where_price = [];
foreach ($_POST['fields']['price'] as $range) {
$value = explode('-', $range);
if (count($value) != 2) {
continue;
}
$min = (int) $value[0];
$max = (int) $value[1];
$where_range = 'CAST(`TVprice`.`value` AS DECIMAL(13,3)) >= ' . $min;
if ($max) {
$where_range .= ' AND CAST(`TVprice`.`value` AS DECIMAL(13,3)) <= ' . $max;
}
$where_price[] = '(' . $where_range . ')';
}
$where[] = '(' . implode(' OR ', $where_price) . ')';
}
break;
default:
if (isset($_POST['fields'][$tv]) && $_POST['fields'][$tv] !== '') {
$where[$tv] = $_POST['fields'][$tv];
}
break;
}
}
// Добавляем это условие в параметры pdoPage "на лету"
$_SESSION['pdoPage'][$hash]['where'] = $where;
$output['message'] = $where;
$output['success'] = true;
} else {
$output['message'] = 'Error';
}
echo $modx->toJSON($output);
die();
break;
default:
break;
}Теперь наши фильтры работают. Единственное — pdoPage «не знает», что показывать, когда ни один результат не найден. Чтобы такой случай учесть, создадим файл pdopage.custom.js и укажем его в параметре frontend_js:
{'!pdoPage' | snippet : [
'ajaxMode' => 'default',
'frontend_js' => '/assets/components/pdotools/js/pdopage.custom.js',
'parents' => 0,
'limit' => 3,
// ...
}В стандартном коде мы добавим только условие else на случай, если ответ от сервера будет пустым:
// ...
pdoPage.loadPage = function (href, config, mode) {
// ...
$.post(config['connectorUrl'], params, function (response) {
if (response && response['total']) {
// ...
} else { // Добавляем условие else
wrapper.find(rows).html('Ничего не найдено');
wrapper.find(pagination).html('');
wrapper.removeClass('loading');
wrapper.css({opacity: 1});
if (config['mode'] == 'default') {
$('html, body').animate({scrollTop: wrapper.position().top - 50 || 0}, 0);
}
}
}, 'json');
};
// ...На этом простой AJAX-фильтр готов.
Объектная
С Уважением,
Алексей
Можете помочь с дополнением таким.
Добавил в вызов условие:
В него передаются параметры:
Т.е. сравниваются значения ТВ у ресурса и выводятся как надо.
Получается ваш метод работает как Ajax, а мои ссылки перезагружают страницу и фильтр по ajax скидывается.
Вот мне нужно это всё как то соединить. Помогите с решением.
Нужна помощь.
Фильтр переделал.
Ниже должно отображаться, то что выбрали в фильтре, т.е. например так?
И ниже:
А только потом уже результаты… Готов отблагодарить.
Подскажите, пожалуйста, а как сделать так, чтобы результат запроса подгружался не «на лету», а после нажатия кнопки? но при этом страница полностью не перезагружалась?
// фильтр цена if(!empty($_POST['fields']['price_from']) && empty($_POST['fields']['price_to'])) { $where['cost:>='] = (int) $_POST['fields']['price_from']; } elseif(!empty($_POST['fields']['price_from']) && !empty($_POST['fields']['price_to']) && (int)$_POST['fields']['price_to'] != 10000) { $where[] = array( 'cost:>=' => (int) $_POST['fields']['price_from'], 'AND:cost:<=' => (int) $_POST['fields']['price_to'] ); }elseif(!empty($_POST['fields']['price_to']) && (int)$_POST['fields']['price_to'] != 10000) { $where['cost:<='] = (int) $_POST['fields']['price_to']; }В результате сейчас получаю два вопроса:
1) при выборе одновременно нескольких чек-боксов в фильтре — выводятся только ресурсы с соответствием только 1 чек-боксу
2) если в ресурсе, в TV отмечено несколько значений, например white||blue, то при выборе фильтра только по чек-боксу white этот товар не будет показан
Буду признателен, как все читатели блога, если вы сможете дополнить функционал.
феном обязательно нужен?
В кэше пдо показывает значение вхере, есил их вписать в ручную при загрузке странице выведет необходимое, но не филттрует, если я включаю феном страница полностью вешается
Сам плагин
<?php switch ($_POST['action']) { case 'filter': $Data = $_POST; $output = array('success' => false, 'message' => ''); // Проверяем, что hash получен и параметры pdoPage существуют if (isset($Data['hash']) && !empty($Data['hash']) && isset($_SESSION['pdoPage'][$Data['hash']]) && !empty($_SESSION['pdoPage'][$Data['hash']])) { $hash = (string) $Data['hash']; // Наполняем условие выборки $where = array(); $ds = ""; foreach($Data['fields'] as $key => $value){ switch ($key){ case 'range': $range_TV = $key['range_field']; $where_filter = []; $min = $key['_from']; $max = $key['_to']; //range:{'range_field':'hui',_from:2500,_to:5236} $where_range = "AST(`TV{$range_TV}`.`value` AS DECIMAL(13,3)) >= {$min}"; if ($max) { $where_range .= " AND CAST(`TV{$range_TV}`.`value` AS DECIMAL(13,3)) <= {$max}"; } $where_filter[] = '(' . $where_range . ')'; $where[] = '(' . implode(' OR ', $where_filter) . ')'; break; default: if (isset($key) && $key !== '' && $key != 'official') { $where[$key] = $value; } break; } } // Добавляем это условие в параметры pdoPage "на лету" $_SESSION['pdoPage'][$hash]['where'] = $modx->toJSON($where); $output['message'] = $where; $output['success'] = true; } else { $output['message'] = 'Error'; } echo $modx->toJSON($output); die(); break; default: break; }сам вывод на странице[[!pdoPage? &parents=`[[*id]]` &tpl=`FilteredItem` &element=`msProducts` &depth=`4` &level=`4` &limit=`9` &where=`{"template:=":5}` &ajaxMode=`default` &ajaxElemWrapper=`#w0` &ajaxElemRows=`#w0 .rows` &ajaxElemPagination=`.pagination` &ajaxElemLink=`.pagination a` &pageLimit=`3` &tplPageFirstEmpty=`` &tplPageLastEmpty=`` &tplPageLast=`` &tplPageActive=`@INLINE <li><a href="[[+href]]">[[+pageNo]]</a></li>` &tplPageFirst=`` &tplPageSkip=`@INLINE <li class="disabled"><span>...</span></li>` &tplPagePrev=`@INLINE <li class="control"><a href="[[+href]]"><</a></li>` &tplPageNext=`@INLINE <li class="control"><a href="[[+href]]">></a></li>` &includeTVs=`tv.image,wheel_diametr,wheel_load_index,wheel_name,wheel_price,wheel_profile,wheel_quantity,wheel_speed_index,wheel_symbol_speed_index,is_summer__or__winter_tyre2,wheel_vendor,wheel_width,wheel_load_index_from` ]]<div id="pdopage"> [[!+page.nav]] <div class="rows"> [[!pdoPage? &parents=`0` &ajaxMode=`button` &limit=`5` ]] </div> </div>Кнопка не отрисовывается в зависиомсти от результата (только при перезагрузки страницы), пример: перезагружаю страницу, кнопка «показать еще», жму на нее до тех пор пока загружать нечего — кнопка прячется. Жму на фильтр — фильтр срабатывает, выводятся нужные ресурсы, но кнопка не появляется, хотя должна, ресурсов больше чем параметре &limit.
Решается это как-то?
И спасибо Илье Уткину за статью, смог сделать фильтрацию по опциям товара
// И отправляем этот массив на сервер. $.post(document.location.href, { action: 'filter', fields: fields, // Параметр hash - обязательный (он содержит настройки pdoPage) hash: pdoPage.configs.page.hash }, function(data) { // Просим pdoPage загрузить новый список ресурсов var tmp = document.location.href.split('?'); // чтобы пагинация пагинила) pdoPage.Reached = false; pdoPage.keys.page = 0; pdoPage.loadPage(tmp[0], pdoPage.configs.page); });$('.btn-more').show() внутри вызова function(data)
в случае с button — она просто пропадает
$.post(document.location.href, { action: 'filter', fields: fields, // Параметр hash - обязательный (он содержит настройки pdoPage) hash: pdoPage.configs.page.hash }, function(data) { // Просим pdoPage загрузить новый список ресурсов var tmp = document.location.href.split('?'); pdoPage.keys.page = 0; pdoPage.loadPage(tmp[0], pdoPage.configs.page); //Добавить строчки ниже pdoPage.callbacks.after = function(config, response) { if (response.total <= config.pageLimit || response.page == response.pages) { $(config.more).hide(); } else { $(config.more).show(); } $('#pdopage').removeClass('loading'); $('#pdopage').css('opacity', 1); } });Вопрос вот в чем. Подскажите, пожалуйста, как реализовать эту фильтрацию со своим id, вместо id=«pdoPage» и с несколькими id, если на странице несколько независимых выводов pdopage, каждый из которых со своей работающей пагинации по кнопке?
{'!pdoPage' | snippet: [
'includeContent'=>1,
'parents' => 35,
'sortby' => 'publishedon',
'sortdir' => 'DESC',
'limit'=> 12,
'totalVar' => 'reviewsPdo_total',
'pageVarKey' => 'reviewsPdo',
'pageNavVar' => 'reviewsPdo_nav',
'ajaxMode' => 'button',
'ajaxElemMore' => '#reviewsPdo .btn-more',
'ajaxTplMore' => '@INLINE Показать еще',
'ajaxElemWrapper'=> '#reviewsPdo',
'ajaxElemRows' => '#reviewsPdo .rows',
'ajaxElemPagination' => '#reviewsPdo .pagination',
'ajaxElemLink' => '#reviewsPdo .pagination a',
'includeTVs' => '',
'tvPrefix' => '',
'tpl' => '@FILE chunk/reviews/reviews__item.tpl',
'toPlaceholder' => 'reviewsPdo',
]}
{if $_modx->getPlaceholder('reviewsPdo_total') != 0}
{$_modx->getPlaceholder('reviewsPdo')}
{$_modx->getPlaceholder('reviewsPdo_nav')}
{/if}
А как быть с migx? По ним в тема не затронута совсем к сожалению. А на практике таких TV встречается много. Как фильтровать по полю таких TV. Какие изменения потребует плагин на примере ниже, если само TV — migx_size, а поле по которому нужно фильтрануть — size
case 'migx_size':
if (isset($_POST['fields']['migx_size']) && $_POST['fields']['migx_size'] !== '') {
$where['migx_size'] = $_POST['fields']['migx_size'];
}
break;
Или он сам срабатывает при отправке запроса?