[tvSuperSelect] Кейс. Удобное указание опций для фильтрации в mFilter2
Часто интернет магазину требуется большое кол-во разделов каталога (500) и такое же большое кол-во опций (200). Заказчик хочет, чтобы:
— опции можно было легко выбирать при редактировании категории товаров,
— на фронте, если у категории не выбрано ни одной опции, брать эти опции из родительской категории, у которой они указаны.
С tvSuperSelect теперь сделать это не так уж сложно, как может показаться.
Подготовка
Нам потребуется miniShop2, mSearch2, tvSuperSelect, Fenom.И даже не просите меня помочь сделать это без Fenom!
Шаг 1
Создаём ТВ поле mfilter_options с типом tvSuperSelect и указываем такие настройки:- Connector URL
/assets/custom/tvssconnector.php
- Процессор
tvss/getms2options
Шаг 2
Создаём файл-коннектор.<?php
require_once dirname(dirname(dirname(__FILE__))) . '/config.core.php';
require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';
$modx->request->handleRequest(array(
'processors_path' => MODX_CORE_PATH . 'custom/processors/',
'location' => '',
));
Местонахождение: /assets/custom/tvssconnector.phpШаг 3
Создаём процессор, в котором будет происходить выборка опций miniShop2 и формирование массива для отображения в списке ComboBox.<?php
class customTvssComboGetMs2OptionsProcessor extends modObjectProcessor
{
public $classKey = 'msOption';
/** @var miniShop2 $ms2 */
protected $ms2;
/** @var tvSuperSelect $tvss */
protected $tvss;
/**
* @return bool
*/
public function initialize()
{
$this->ms2 = $this->modx->getService('minishop2', 'miniShop2', MODX_CORE_PATH . 'components/minishop2/model/minishop2/');
$this->tvss = $this->modx->getService('tvsuperselect', 'tvSuperSelect',
$this->modx->getOption('tvsuperselect_core_path', null, MODX_CORE_PATH . 'components/tvsuperselect/') . 'model/tvsuperselect/');
return parent::initialize();
}
/**
* @return string
*/
public function process()
{
$query = trim($this->getProperty('query'));
$limit = (int)$this->getProperty('limit', 0);
$resource_id = (int)$this->getProperty('resource_id', 0);
if (!$tv_id = (int)$this->getProperty('tv_id', 0)) {
//
}
$q = $this->modx->newQuery($this->classKey);
$q->select(array(
"{$this->classKey}.key as `key`",
"{$this->classKey}.caption as `caption`",
));
if (!empty($query)) {
$q->where(array(
"{$this->classKey}.key:LIKE" => "%{$query}%",
"OR:{$this->classKey}.caption:LIKE" => "%{$query}%"
));
}
$q->limit($limit);
$q->sortby($this->classKey . '.key', 'ASC');
$rows = array();
if ($q->prepare() && $q->stmt->execute()) {
if ($tmp = $q->stmt->fetchAll(PDO::FETCH_ASSOC)) {
foreach ($tmp as $v) {
$rows[] = array(
'display' => '(' . $v['key'] . ') <b>' . $v['caption'] . '</b>',
'value' => $v['key'],
);
}
}
}
foreach ($rows as &$row) {
if (empty($row['display'])) {
$row['display'] = $row['value'];
}
}
unset($row);
return $this->outputArray($rows);
}
}
return 'customTvssComboGetMs2OptionsProcessor';
Местонахождение: /core/custom/processors/tvss/getms2options.class.phpШаг 4
Формирование списка опций пригодного для mFilter2 и вывод на фронте будет происходить при помощи Fenom.У вас должно получиться, что-то вроде этого:
{var $filters = []}
{* Мы можем указать свойства фильтра, которые отобразятся до опций, выбранных нами *}
{var $filters[] = 'ms|price:number'}
{var $filters[] = 'parent:categories'}
{* Магия *}
{var $mfilter_options = $_modx->resource['mfilter_options']}
{if !is_array($mfilter_options)}
{var $mfilter_options = ($mfilter_options | fromJSON)}
{/if}
{if $mfilter_options is empty}
{foreach $_modx->getParentIds($_modx->resource['id']) as $parent_id}
{if $parent_id == 0}
{continue}
{/if}
{var $mfilter_options = ($parent_id | resource : 'mfilter_options')}
{if !is_array($mfilter_options) AND $mfilter_options?}
{var $mfilter_options = ($mfilter_options | fromJSON)}
{/if}
{if $mfilter_options?}
{break}
{/if}
{unset $mfilter_options}
{/foreach}
{/if}
{if $mfilter_options?}
{foreach $mfilter_options as $v}
{var $filters[] = ('msoption|' ~ $v)}
{/foreach}
{/if}
{* Вывод mFilter2 *}
{'!mFilter2' | snippet : [
...
'filters' => ($filters | join : ','),
...
]}
Итого
Как видите, всё довольно просто с новой версией tvSuperSelect. С этим функционалом теперь можно и не такое сделать!
Поблагодарить автора
Отправить деньги
Комментарии: 13
Павел, спасибо за примеры! Отличное дополнение
Годный кейс, спасибо!
Паша, за примеры, ОГРОМНОЕ спасибо.
Очень хочется, чтобы поле работало в migx таблице.
Очень хочется, чтобы поле работало в migx таблице.
Оно не работает?
Не сохраняет данные.
что-то не так на стороне js, потому что процессор migx ловит пустое значение. оно тупо не отправляется на сохранение.
у меня две тв-шки рядом: «video» (listbox-multiply) и «gallery» (tvsuperselect)
в migx сырые данные приходят в виде json, и этот кусок выглядит так:
у меня две тв-шки рядом: «video» (listbox-multiply) и «gallery» (tvsuperselect)
в migx сырые данные приходят в виде json, и этот кусок выглядит так:
{"video":["1","2","3"],"gallery":""}
что касается поля migx внутри migx (которое тоже json по идее), то оно экранируется и приходит вот так:{"somemigxfield":"[{\"field\":\"value\"}]"}
и думается мне, что tvsuperselect тоже надо экранировать и строкой отправлять, только где и когда это отловить пока не разобрался
Добавил поддержку MIGX.
modx.pro/components/16719
modx.pro/components/16719
Сделал по интструкции но modx автоматически определяет фильтры как чекбоксы а надо бы слайдер и селекты такое уже не реализовать?
Это указывается непосредственно в сниппете mFilter2 в качестве параметра шаблона для конкретной опции фильтра.
получается не совсем универсально, ведь если появились опции новые пользователь не зайдет и не добавит сам шаблон
Так и опции сами не добавятся.
это можно сделать из админки и простому пользователю
Дополнил скрипт, теперь при создании опции с типом number скрипт распознает ее и вместо вывода чекбоксами будет выводить ее бегунками от — до:) можно расширить функционал как душе угодно я добавил в getms2options.class.php switch по типу опции где помимо number можно указать и другие типы и следовательно шаблоны к ним:)
Шаг 3 заменил на это:
Шаг 4 заменил на это:
Шаг 3 заменил на это:
<?php
class customTvssComboGetMs2OptionsProcessor extends modObjectProcessor
{
public $classKey = 'msOption';
/** @var miniShop2 $ms2 */
protected $ms2;
/** @var tvSuperSelect $tvss */
protected $tvss;
/**
* @return bool
*/
public function initialize()
{
$this->ms2 = $this->modx->getService('minishop2', 'miniShop2', MODX_CORE_PATH . 'components/minishop2/model/minishop2/');
$this->tvss = $this->modx->getService('tvsuperselect', 'tvSuperSelect',
$this->modx->getOption('tvsuperselect_core_path', null, MODX_CORE_PATH . 'components/tvsuperselect/') . 'model/tvsuperselect/');
return parent::initialize();
}
/**
* @return string
*/
public function process()
{
$query = trim($this->getProperty('query'));
$limit = (int)$this->getProperty('limit', 0);
$resource_id = (int)$this->getProperty('resource_id', 0);
if (!$tv_id = (int)$this->getProperty('tv_id', 0)) {
//
}
$q = $this->modx->newQuery($this->classKey);
$q->select(array(
"{$this->classKey}.key as `key`",
"{$this->classKey}.caption as `caption`",
"{$this->classKey}.type as `type`",
));
if (!empty($query)) {
$q->where(array(
"{$this->classKey}.key:LIKE" => "%{$query}%",
"OR:{$this->classKey}.caption:LIKE" => "%{$query}%"
));
}
$q->limit($limit);
$q->sortby($this->classKey . '.key', 'ASC');
$rows = array();
if ($q->prepare() && $q->stmt->execute()) {
if ($tmp = $q->stmt->fetchAll(PDO::FETCH_ASSOC)) {
foreach ($tmp as $v) {
switch ($v['type']) {
case 'numberfield':
$v['type'] = 'number';
break;
default:
$v['type'] = '';
break;
}
if ($v['type'] != '') {
$rows[] = array(
'display' => '(' . $v['key'] . ') <b>' . $v['caption'] . '</b>',
'value' => $v['key'] . ':' . $v['type'],
);
} else {
$rows[] = array(
'display' => '(' . $v['key'] . ') <b>' . $v['caption'] . '</b>',
'value' => $v['key'],
);
}
}
}
}
foreach ($rows as &$row) {
if (empty($row['display'])) {
$row['display'] = $row['value'];
}
}
unset($row);
return $this->outputArray($rows);
}
}
return 'customTvssComboGetMs2OptionsProcessor';
Шаг 4 заменил на это:
{var $filters = []}
{* Мы можем указать свойства фильтра, которые отобразятся до опций, выбранных нами *}
{var $filters[] = 'ms|vendor:vendors'}
{var $filters[] = 'msop|price:number'}
{* Магия *}
{var $mfilter_options = $_modx->resource['mfilter_options']}
{if !is_array($mfilter_options)}
{var $mfilter_options = ($mfilter_options | fromJSON)}
{/if}
{if $mfilter_options is empty}
{foreach $_modx->getParentIds($_modx->resource['id']) as $parent_id}
{if $parent_id == 0}
{continue}
{/if}
{var $mfilter_options = ($parent_id | resource : 'mfilter_options')}
{if !is_array($mfilter_options) AND $mfilter_options?}
{var $mfilter_options = ($mfilter_options | fromJSON)}
{/if}
{if $mfilter_options?}
{break}
{/if}
{unset $mfilter_options}
{/foreach}
{/if}
{if $mfilter_options?}
{foreach $mfilter_options as $v}
{var $filters[] = ('msoption|' ~ $v)}
{/foreach}
{/if}
{* Все параметры mFilter2 указываем тут *}
{var $options = [
'element'=>'msProducts',
'parents'=> $_modx->resource.id,
'setTotal'=>'1',
'totalVar'=>'total',
'loadModels'=>'easycomm',
'includeTVs'=>'smallDescription',
'tplOuter'=>'tpl.catalogWrap',
'tpl'=>'catalog.Row',
'limit'=>'20',
'ajaxMode'=>'button',
'includeThumbs'=>'small',
'filterOptions'=>'{
"more_tpl":"<button class=\"standart-button align-center how-more-button btn_more btn w-button\">Показать ещё 20 товаров<\/button>"
}',
'leftJoin'=>'{
"ecThread": {
"class": "ecThread",
"on": "msProduct.id = ecThread.resource"
}
}',
'aliases'=>'
ms|price==price,
resource|parent==parent,
ms|vendor==vendor
',
'sortAliases'=>'{
"ecThread":"ecThread"
}',
'sort'=>'ms|menuindex:asc',
'suggestionsRadio'=>'vendor:vendors',
'class'=>'msProduct',
'filters' => ($filters | join : ','),
'tplFilter.outer.msop|price'=>'tpl.mFilter2.filter.slider',
'tplFilter.row.msop|price'=>'tpl.mFilter2.filter.number',
'tplFilter.outer.vendor'=>'tpl.mfilter2.filter.select',
'tplFilter.row.vendor'=>'tpl.mfilter2.filter.option'
]}
{var $tplFilters = []}
{foreach $filters as $row}
{if ':number' | in : $row}
{* тут мы проверяем какого типа у нас опция *}
{var $row = $row | replace : ":number" : ""}
{* удалив название типа из строки формируем шаблоны для вывода *}
{set $tplFilters['tplFilter.outer.' ~ $row] = 'tpl.mFilter2.filter.slider'}
{set $tplFilters['tplFilter.row.' ~ $row] = 'tpl.mFilter2.filter.number'}
{/if}
{/foreach}
{* Соединяем два массива - один с опциями mfilter2 другой c нашими шаблонами ОБЯЗАТЕЛЬНО должен быть включен параметр "использование PHP в FENOM, если кто знает как выполнить слияние двух массивов средствами Fenom иначе - поделитесь *}
{set $options = array_merge($tplFilters, $options)}
{* Вывод mFilter2 *}
{'!mFilter2' | snippet : $options}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.