Skip to content

Instantly share code, notes, and snippets.

@andronex
Last active December 18, 2020 14:17
Show Gist options
  • Save andronex/b5f95c50f1a8b453435f1cb7d07a3ad1 to your computer and use it in GitHub Desktop.
Save andronex/b5f95c50f1a8b453435f1cb7d07a3ad1 to your computer and use it in GitHub Desktop.
Фильтрация с предсказанием кол-ва результатов. //TODO
var mSearch2 = {},
selectorFilter = '.js-filter',
selectorInput = selectorFilter+' input',
selectorClear = selectorFilter+' .js-filter-clean',
selectorExe = selectorFilter+' .js-execute',
selectorToggle = selectorFilter+' .js-filter-toggle',
selectorFormWrap = selectorFilter+' .js-filter-form-wrap',
selectorRows = '.js-rows';
mSearch2.Hash = {
get: function() {
var vars = {}, hash, splitter, hashes;
if (!this.oldbrowser()) {
var pos = window.location.href.indexOf('?');
hashes = (pos != -1) ? decodeURIComponent(window.location.href.substr(pos + 1)) : '';
splitter = '&';
}
else {
hashes = decodeURIComponent(window.location.hash.substr(1));
splitter = '/';
}
if (hashes.length == 0) {return vars;}
else {hashes = hashes.split(splitter);}
for (var i in hashes) {
if (hashes.hasOwnProperty(i)) {
hash = hashes[i].split('=');
if (typeof hash[1] == 'undefined') {
vars['anchor'] = hash[0];
}
else {
vars[hash[0]] = hash[1];
}
}
}
return vars;
}
,set: function(vars) {
var hash = '';
for (var i in vars) {
if (vars.hasOwnProperty(i)) {
hash += '&' + i + '=' + vars[i];
}
}
if (!this.oldbrowser()) {
if (hash.length != 0) {
hash = '?' + hash.substr(1);
}
window.history.pushState(hash, '', document.location.pathname + hash);
}
else {
window.location.hash = hash.substr(1);
}
}
,add: function(key, val) {
var hash = this.get();
hash[key] = val;
this.set(hash);
}
,remove: function(key) {
var hash = this.get();
delete hash[key];
this.set(hash);
}
,clear: function() {
let hashs = this.get();
let hash = {};
for(var k in hashs) {
if(k == 'text' || k == 'video'){
hash[k] = hashs[k];
}
}
this.set(hash);
}
,oldbrowser: function() {
return !(window.history); // && history.pushState);
}
};
var setHashs = function(){
if((hashs = mSearch2.Hash.get()) && $( selectorFilter ).length){
let isset = false;
for(let k in hashs) {
if( $('input[name='+k+']').length ){
$('input[name='+k+']').prop('checked', false);
isset = true;
array = hashs[k].toString().split(',');
for (let i = 0; i < array.length; i++) {
$('input[name='+k+'][value='+array[i]+']').prop('checked', true);
}
}
}
if(isset){
$(selectorToggle).addClass('open');
$(selectorFormWrap).show();
}
}
}
var getProps = function (obj, name = '') {
for (var property in obj) {
if (obj.hasOwnProperty(property) && obj[property] != null) {
if (obj[property].constructor == Object) {
getProps(obj[property], property);
} else if (obj[property].constructor == Array) {
for (var i = 0; i < obj[property].length; i++) {
getProps(obj[property][i]);
}
} else {
//console.log( $(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]') );
$(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]').prop('disabled', false);
$(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]').parent().find('.js-num').text(obj[property]);
if(obj[property] == 0){
if($(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]').is(':checked')){
$(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]').trigger('click');
}
$(selectorFilter).find('input[name="'+name+'"][value="'+property+'"]').prop('disabled', true);
}
}
}
}
}
var exeFilter = function(th){
var $this = $(th),
otherInp = $this.parents('form').find('input:checkbox:not(:checked)'),
other = [],
checkInp = $this.parents('form').find('input:checkbox:checked'),
check = [];
otherInp.each(function(){
other.push({"name":$(this).prop('name'), "value":$(this).val()});
});
checkInp.each(function(){
check.push({"name":$(this).prop('name'), "value":$(this).val()});
});
$this.prop('disabled', true);
$(selectorRows).css({"opacity":"0.5"});
var hashs = mSearch2.Hash.get(),
count = Object.keys(hashs).length;
hashs["action"] = 'filter';
hashs["other"] = other;
hashs["check"] = check;
hashs["parent"] = $(selectorFormWrap).find('[name=parent]').val()||4;
$.ajax({
type: 'POST',
url: '/assets/components/wefilter/action.php',
dataType: 'json',
data: hashs,
success: function (x) {
if(x.success){
if(count){
$(selectorRows).html(x.data?x.data:'<h3>По данным параметрам ничего не найдено</h3>');
$(selectorRows).css({"opacity":"1"});
$this.prop('disabled', false);
}
else{
$(selectorRows).load(location.href+" "+selectorRows+" >*",function(html, success){
$(selectorRows).css({"opacity":"1"});
$this.prop('disabled', false);
});
}
if(x.suggestion){
//getProps(x.suggestion);
$(selectorFormWrap).css({"opacity":"0.5"});
if(x.data){
hashs["action"] = 'suggest';
$.ajax({
type: 'POST',
url: '/assets/components/wefilter/action.php',
dataType: 'json',
data: hashs,
success: function (x) {
if(x.success){
getProps(x.suggestion);
}
$(selectorFormWrap).css({"opacity":"1"});
}
});
}
}
}
else{
$(selectorRows).css({"opacity":"1"});
$this.prop('disabled', false);
}
}
});
}
$(document).ready(function() {
//сброс фильтра
$(document).on('click touch', selectorClear, function(){
mSearch2.Hash.clear();
$(selectorInput).prop('checked', false);
exeFilter(selectorExe);
});
//установка фильтров при посещении страницы
if( $(selectorExe).length ){
setHashs();
exeFilter(selectorExe);
}
//фильтрация в каталоге
$(document).on('change', selectorInput, function(){
let name = $(this).attr('name'),
val = $(this).val(),
hashs = mSearch2.Hash.get(),
isset = false;
//console.log(hashs);
for(let k in hashs) {
if(k == name && val && $(this).prop('checked')){
isset = true;
array = hashs[k].toString().split(',');
array.push(val);
array = array.filter(item => item != '');
hashs[k] = array.join();
}
if(k == name && (!$(this).prop('checked'))){
isset = true;
array = hashs[k].toString().split(',');
array = array.filter(item => (item != val && item != ''));
hashs[k] = array.join();
if(!hashs[k].length){
delete hashs[k];
}
}
}
//console.log(hashs);
if(!isset && val && $(this).prop('checked')){
mSearch2.Hash.add(name, val);
hashs = mSearch2.Hash.get();
}
if(!isset && (!$(this).prop('checked') || val == '')){
mSearch2.Hash.remove(name);
hashs = mSearch2.Hash.get();
}
mSearch2.Hash.set(hashs);
exeFilter(selectorExe);
});
//применение фильтра в каталоге
$(document).on('click touch', selectorExe, function(e){
e.preventDefault();
exeFilter(selectorExe);
});
});
<?php
header('Access-Control-Allow-Origin: *');
header('Content-type: application/json; charset=utf-8');
if (!isset($modx)) {
define('MODX_API_MODE', true);
while (!isset($modx) && ($i = isset($i) ? --$i : 10)) {
if (($file = dirname(!empty($file) ? dirname($file) : __FILE__) . '/index.php') AND !file_exists($file)) {
continue;
}
require_once $file;
}
if (!is_object($modx)) {
exit('{"success":false,"message":"Access denied"}');
}
$modx->getService('error', 'error.modError');
$modx->getRequest();
$modx->setLogLevel(modX::LOG_LEVEL_ERROR);
$modx->setLogTarget('FILE');
$modx->error->message = null;
}
$ctx = !empty($_REQUEST['ctx']) ? $_REQUEST['ctx'] : $modx->context->get('key');
if ($ctx != $modx->context->get('key')) {
$modx->switchContext($ctx);
}
function Sanitize($input = '', $length = 0){
global $modx;
if (is_array($input)) {
$output = array();
foreach ($input as $key => $value) {
$output[$key] = Sanitize($value, $length);
}
} else {
$expr = $modx->getOption('office_sanitize_pcre', null, '/[^-_0-9\p{L}\s@.,:\/\\+()]+/iu', true);
$string = html_entity_decode($input, ENT_QUOTES, 'UTF-8');
$output = trim(preg_replace($expr, '', $string));
return !empty($length)
? mb_substr($output, 0, $length, 'UTF-8')
: $output;
}
return $input;
};
$data = Sanitize($_POST);
$action = $data['action']?:'';
$other = $data['other']?:array();
$check = $data['check']?:array();
$is_success = false;
unset($data['action'], $data['other']);
$output = '';
$count = 0;
$suggestion = array();
function setWhere($query, $data, &$suggestion = array()){
global $modx, $ctx;
foreach($data as $key => $val){
$val = array_map('trim', explode(',', $val));
switch($key){
case 'parent':
$parents = $val;
foreach($val as $v){
$parents = array_merge($parents, $modx->getChildIds($v, 10, array('context' => $ctx)));
}
$exclude = array(1510, 906, 907);
foreach($exclude as $v){
$exclude = array_merge($exclude, $modx->getChildIds($v, 10, array('context' => $ctx)));
}
$parents = array_diff($parents, $exclude);
$where = array();
$where[ $key.':IN' ] = $parents;
$query->where($where);
//$query_all->where($where);
break;
case 'length':
$where = array();
foreach($val as $v){
$where[][ 'OR:'.$key.'.value:LIKE' ] = '%x'.$v.'x%';
$suggestion[$key][$v] = -1;
}
//$select[] = 'COALESCE(`length`.`value`, 0) as `length`';
$query->where($where);
break;
case 'load':
$where = array();
foreach($val as $v){
$where[][ 'OR:'.$key.'.value:=' ] = $v;
$suggestion[$key][$v] = -1;
}
//$select[] = 'COALESCE(`'.$key.'`.`value`, 0) as `'.$key.'`';
$query->where($where);
break;
case 'stand':
$where = array();
foreach($val as $v){
$where[][ 'OR:'.$key.'.value:=' ] = $v;
$suggestion[$key][$v] = -1;
if($v == 0){
$where[][ 'OR:'.$key.'.value:IS' ] = NULL;
}
}
//$select[] = 'COALESCE(`'.$key.'`.`value`, 0) as `'.$key.'`';
$query->where($where);
break;
}
}
return $query;
}
switch($action){
case 'suggest':
$suggestion_etalon = array();
$query = $modx->newQuery('msProduct', array('class_key:=' => 'msProduct', 'published:=' => 1, 'deleted:=' => 0, 'hidemenu:=' => 0));
$query->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query = setWhere($query, $data);
if($query->prepare() && $query->stmt->execute()){
if($ids = $query->stmt->fetchAll(PDO::FETCH_ASSOC)){
$count = count($ids);
foreach($ids as $item){
if( $item['length'] ){
$output_array = array();
preg_match('/x(?P<length>\d+)x/', $item['length'], $output_array);
if($output_array['length']){
$suggestion_etalon['length'][$output_array['length']] = isset($suggestion_etalon['length'][$output_array['length']])?$suggestion_etalon['length'][$output_array['length']]+1:1;
}
}
if( isset($item['load']) ){
$suggestion_etalon['load'][$item['load']] = isset($suggestion_etalon['load'][$item['load']])?$suggestion_etalon['load'][$item['load']]+1:1;
}
if( isset($item['stand']) ){
$suggestion_etalon['stand'][$item['stand']] = isset($suggestion_etalon['stand'][$item['stand']])?$suggestion_etalon['stand'][$item['stand']]+1:1;
}
}
}
}
foreach(array_merge($other, $check) as $o){
$prop = $data;
$prop[ $o['name'] ] = $o['value'];
$query = $modx->newQuery('msProduct', array('class_key:=' => 'msProduct', 'published:=' => 1, 'deleted:=' => 0, 'hidemenu:=' => 0));
$query->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query = setWhere($query, $prop);
if($query->prepare() && $query->stmt->execute()){
if($ids = $query->stmt->fetchAll(PDO::FETCH_ASSOC)){
$suggestion_etalon[ $o['name'] ][ $o['value'] ] = count($ids);
}
else $suggestion_etalon[ $o['name'] ][ $o['value'] ] = 0;
}
else $suggestion_etalon[ $o['name'] ][ $o['value'] ] = 0;
}
$is_success = true;
$suggestion = $suggestion_etalon;
break;
case 'filter':
$where = array();
//$select = array();
$query = $modx->newQuery('msProduct', array('class_key:=' => 'msProduct', 'published:=' => 1, 'deleted:=' => 0, 'hidemenu:=' => 0));
$query->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query_all = $modx->newQuery('msProduct', array('class_key' => 'msProduct'));
$query_all->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query_all->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query_all->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query_all->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query_all = setWhere($query_all, $data);
$suggestion_etalon = array();
if($query_all->prepare() && $query_all->stmt->execute()){
if($ids = $query_all->stmt->fetchAll(PDO::FETCH_ASSOC)){
foreach($ids as $item){
if( $item['length'] ){
$output_array = array();
preg_match('/x(?P<length>\d+)x/', $item['length'], $output_array);
if($output_array['length']){
$suggestion_etalon['length'][$output_array['length']] = isset($suggestion_etalon['length'][$output_array['length']])?$suggestion_etalon['length'][$output_array['length']]+1:1;
}
}
if( isset($item['load']) ){
$suggestion_etalon['load'][$item['load']] = isset($suggestion_etalon['load'][$item['load']])?$suggestion_etalon['load'][$item['load']]+1:1;
}
if( isset($item['stand']) ){
$suggestion_etalon['stand'][$item['stand']] = isset($suggestion_etalon['stand'][$item['stand']])?$suggestion_etalon['stand'][$item['stand']]+1:1;
}
}
}
}
$query = setWhere($query, $data, $suggestion);
if($query->prepare() && $query->stmt->execute()){
$is_success = true;
if($ids = $query->stmt->fetchAll(PDO::FETCH_ASSOC)){
$count = count($ids);
$resources = array();
foreach($ids as $item){
$resources[] = $item['id'];
}
$message = 'Фильтр применён';
$output = $modx->runSnippet('msProducts', array(
'resources' => implode(',', $resources),
'parents' => $parents?implode(',', $parents):4,
'includeTVs' => 'image',
'limit' => 0,
'sortby' => 'menuindex',
'sortdir' => 'ASC'
));
$output .= '<li style="clear:both"></li>';
foreach($other as $o){
$prop = $data;
if(isset( $prop[ $o['name'] ] )){
$val = array_map('trim', explode(',', $prop[ $o['name'] ]));
$val[] = $o['value'];
$prop[ $o['name'] ] = implode(',', $val);
}
else{
$prop[ $o['name'] ] = $o['value'];
}
$query = $modx->newQuery('msProduct', array('class_key:=' => 'msProduct', 'published:=' => 1, 'deleted:=' => 0, 'hidemenu:=' => 0));
$query->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query = setWhere($query, $prop, $suggestion);
if($query->prepare() && $query->stmt->execute()){
if($ids = $query->stmt->fetchAll(PDO::FETCH_ASSOC)){
if(count($ids) > $count){
$suggestion[ $o['name'] ][ $o['value'] ] = count($ids)-$count;
}
else{
$suggestion[ $o['name'] ][ $o['value'] ] = !$suggestion_etalon[ $o['name'] ][ $o['value'] ]?0:count($ids);
}
}
else{
$suggestion[ $o['name'] ][ $o['value'] ] = 0;
}
}
else{
$suggestion[ $o['name'] ][ $o['value'] ] = 0;
}
//print_r($prop);
}
foreach($suggestion as $key => &$sugs){
$tmp = $data;
unset($tmp['parent']);
foreach($sugs as $p => &$sug){
$prop = array('parent' => $data['parent'], $key => $p);
$query = $modx->newQuery('msProduct', array('class_key:=' => 'msProduct', 'published:=' => 1, 'deleted:=' => 0, 'hidemenu:=' => 0));
$query->leftJoin('msProductOption', 'length', '`length`.`product_id` = `msProduct`.`id` AND `length`.`key` = "size"');
$query->leftJoin('msProductOption', 'load', '`load`.`product_id` = `msProduct`.`id` AND `load`.`key` = "load"');
$query->leftJoin('msProductOption', 'stand', '`stand`.`product_id` = `msProduct`.`id` AND `stand`.`key` = "stand"');
$query->select(array_merge(array(
'`msProduct`.`id`'
), array(
'COALESCE(`length`.`value`, 0) as `length`',
'COALESCE(`load`.`value`, 0) as `load`',
'COALESCE(`stand`.`value`, 0) as `stand`',
)));
$query = setWhere($query, $prop);
if($query->prepare() && $query->stmt->execute()){
if($ids = $query->stmt->fetchAll(PDO::FETCH_ASSOC)){
if(isset($data[$key])){
$val = array_map('trim', explode(',', $data[$key]));
if(count($val) > 1 && in_array($p, $val)){
//print_r($p);
//$suggestion[ $key ][ $p ] = $suggestion_etalon[ $key ][ $p ]-count($ids)+$suggestion[ $key ][ $p ];
if(count($tmp) == 1){
$suggestion[ $key ][ $p ] = count($ids);
}
}
}
//$suggestion[ $key ][ $p ] = count($ids);
}
else{
$suggestion[ $key ][ $p ] = 0;
}
}
else{
$suggestion[ $key ][ $p ] = 0;
}
if($sug == -1){
$sug = $count;
}
}
}
}
}
break;
}
$out = array('success' => $is_success, 'message' => $message?:'Не могу обработать запрос', 'data' => $output, 'suggestion' => $suggestion, 'count' => $count);
die(json_encode($out, JSON_FORCE_OBJECT));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment