Skip to content
mFilter
mFilter
Фасетная фильтрация для MODX 3 с поддержкой SEO URL
  • MODX 3
  • PHP 8.1
  • Vue 3
  1. Компоненты
  2. mFilter
  3. Фронтенд
  4. Плейсхолдеры

Плейсхолдеры ​

Какие плейсхолдеры ставят плагин и сниппеты mFilter и как читать их в шаблоне ресурса и в чанках.

Синтаксис ​

Плейсхолдер mfilter.something читается тремя способами:

  • MODX-тег: [[+mfilter.something]]
  • Fenom, явным вызовом: {$_modx->getPlaceholder('mfilter.something')}
  • Fenom, через массив $_pls: {$_pls['mfilter.something']} — только то, что поставлено до начала разбора шаблона, см. ниже

Внимание

Запись {$mfilter.something} — через точку от переменной $mfilter — не работает: такой переменной в Fenom нет.

$_pls в шаблоне и в чанке — это разные массивы ​

$_pls — не сами плейсхолдеры MODX, а их копия, которую pdoTools снимает в начале разбора шаблона или чанка. $_modx->getPlaceholder() читает текущее значение.

ГдеЧто лежит в $_plsКак читать плейсхолдер mfilter.hash
Шаблон ресурсаплейсхолдеры, поставленные до начала разбора шаблона, — то есть плагином{$_modx->getPlaceholder('mfilter.hash')} ниже вызова mFilter
Чанк, отрисованный через getChunk (tplOuter, tpl, tplPagination и прочие)только данные, переданные этому чанку{$hash} — как переменная

pdoTools разбирает шаблон за один проход. Сниппет mFilter, вызванный внутри шаблона, ставит свои плейсхолдеры, когда копия в $_pls уже снята. Через $_pls их не видно даже ниже вызова — читайте через $_modx->getPlaceholder() или MODX-тегом:

fenom
{set $results = '!mFilter' | snippet : ['element' => 'msProducts', 'parents' => $_modx->resource.id]}

{$_pls['mfilter.hash']}                    {* пусто *}
{$_modx->getPlaceholder('mfilter.hash')}   {* хеш вызова *}

Ещё две типовые ошибки:

  • {$_pls['hash']} в шаблоне — пусто: глобальный плейсхолдер называется mfilter.hash, ключа hash там нет. Так выглядит разметка, скопированная из чанка mfilter.outer в шаблон.
  • {$_pls['mfilter.sort']} в чанке — пусто: чанку передана переменная $currentSort, а глобальных плейсхолдеров в его $_pls нет.

В чанке надёжнее брать переменную чанка. Если её нет, глобальный плейсхолдер доступен через {$_modx->getPlaceholder()} или MODX-тегом.

Не дублируйте обёртку результатов в шаблоне

Обёртку с data-mfilter-results рисует чанк tplOuter. Если скопировать её ещё и в шаблон, на странице окажется два вложенных [data-mfilter-results], и JS привяжется к внешнему — из шаблона. Ссылки пагинации внутри чанка тогда идут мимо AJAX: каждый клик перезагружает страницу целиком.

Если обёртка осталась только в шаблоне, data-mfilter-hash в ней пуст — переменные чанка в шаблоне не существуют. Без хеша страница не фильтрует вовсе, см. обязательные точки подключения.

Глобальные плейсхолдеры ​

Их ставят двое:

  • плагин mFilter — при разборе адреса, до шаблона. На любой странице — значения по умолчанию: пустые фильтры и SEO, сортировка pagetitle-asc, лимит 24, вид tpl1. На странице каталога — значения из SEO-адреса, а лимит и сортировка без адреса — из настроек;
  • сниппет mFilter — во время отрисовки. Перезаписывает значения плагина значениями своего вызова.

Что лежит в плейсхолдерах до вызова сниппета и после:

ПлейсхолдерПлагин, любая страницаПлагин, страница каталогаСниппет
Фильтры — mfilter.filtersпустоиз SEO-адресане ставит
Фильтры — mfilter.activeFiltersне ставитиз SEO-адресаиз SEO-адреса и строки запроса
Сортировка — mfilter.sortpagetitle-ascиз SEO-адреса, без него — из настроекзначение вызова
Лимит — mfilter.limit24из SEO-адреса, без него — из настроекзначение вызова, например 12
Вид — mfilter.tpltpl1tpl1 — вид в SEO-адрес не попадаеттекущий вид из &tpls
SEO — mfilter.seo.*пустона SEO-адресе с фильтрами, иначе пустовсегда

Читайте их ниже вызова сниппета через $_modx->getPlaceholder() — тогда получите значения сниппета.

Активные фильтры и URL ​

ПлейсхолдерТипОписание
mfilter.activeFiltersarrayАктивные фильтры — из SEO-адреса и из строки запроса. Значения настоящие: у производителя — id, а не слаг
mfilter.filtersarrayАктивные фильтры только из SEO-адреса. С фильтрами в строке запроса пуст. Ставит плагин
mfilter.base_uristringАдрес страницы каталога без сегмента фильтров, без / в начале и в конце: catalog. Ставит плагин, только на странице каталога
mfilter.filter_uristringСегмент фильтров, тоже без / по краям: brand--apple/color--red
mfilter.baseIdsarrayID всех товаров вызова до применения фильтров. Ставится только с &element; нужен сниппету mFilterForm, поэтому форму вызывают ниже mFilter

Сортировка и пагинация ​

ПлейсхолдерТипОписание
mfilter.sortstringТекущая сортировка: "pagetitle-asc", "price-desc"
mfilter.sortBystringТолько поле: "pagetitle", "price"
mfilter.sortDirstringТолько направление: "asc" / "desc"
mfilter.limitintТекущий лимит на странице
mfilter.defaultLimitintЛимит вызова сниппета — чтобы отличить его от выбранного посетителем
mfilter.pageintТекущая страница
mfilter.tplstringТекущий вид из &tpls ("tpl1", "tpl2")
mfilter.total, mfilter.pageCountintВсего найдено и всего страниц. Имена меняются параметрами &totalVar и &pageCountVar

До вызова сниппета в этих плейсхолдерах — значения плагина (см. выше). Значения вызова — например, 'limit' => 12 — появятся только после сниппета.

SEO ​

Сниппет ставит их всегда. Плагин считает их заранее только на SEO-адресе с фильтрами, в остальных случаях оставляет пустыми.

С фильтрами значения берутся из SEO-шаблона или индивидуальной SEO-страницы. Если нет ни того ни другого, заголовок собирается из заголовка ресурса и значений фильтров.

ПлейсхолдерТипОписание
mfilter.seo.titlestringЗаголовок для <title>. Без фильтров — «заголовок | название сайта»
mfilter.seo.h1stringЗаголовок <h1>. Без фильтров — заголовок ресурса
mfilter.seo.descriptionstringМета description. Без фильтров — описание ресурса
mfilter.seo.canonicalstringКанонический адрес. Без фильтров — полный адрес ресурса; при noindex указывает на страницу без фильтров
mfilter.seo.noindexboolФлаг noindex (см. Системные настройки)
mfilter.seo.textstringПроизвольный SEO-текст

Прочее ​

ПлейсхолдерТипОписание
mfilter.hashstringХеш конфигурации вызова mFilter; по нему AJAX воспроизводит тот же вызов
mfilter.resultsstringHTML карточек — только без &element и при &toPlaceholders=1
mfilter.paginationstringHTML пагинации — там же
mse.query, mse.totalstring, intПоисковая фраза и сколько товаров нашёл поиск — с &element и mSearch, см. mFilter
mfilter.form, mfilter.activeCountstring, intHTML формы и число применённых фильтров — ставит mFilterForm, см. Плейсхолдеры
mfilter.selected.hashstringХеш вызова mFilterSelected

toPlaceholders=1

С &toPlaceholders=1 сниппет mFilter выводит mfilter.results и mfilter.pagination без оборачивающего чанка tplOuter. Теряются data-mfilter-results, data-page-count и вся начальная разметка с сервера — скрипт не сможет её подхватить. Используйте обычный вывод сниппета и оборачивающий чанк tplOuter. С &element параметр ничего не делает.

После фильтрации без перезагрузки ​

После AJAX-запроса плейсхолдеров нет: шаблон ресурса и tplOuter не перерисовываются. Скрипт обновляет на странице:

  • выдачу, пагинацию, число в .mfilter-total и блок .mfilter-empty;
  • <title>, мета description, canonical и robots;
  • элементы с data-mfilter-h1 и data-mfilter-seo-text;
  • форму, кнопку сброса и блок «Выбрано».

Всё остальное, что выведено из плейсхолдеров, — крошки, условия {if}, подписи — остаётся как при загрузке страницы.

Использование в шаблоне ресурса ​

Вызовите mFilter в начале шаблона, сохраните вывод в переменную и выведите его ниже. Тогда плейсхолдеры сниппета читаются через $_modx->getPlaceholder() во всём шаблоне, включая <head>.

SEO-теги в <head> ​

html
{set $results = '!mFilter' | snippet : ['element' => 'msProducts', 'parents' => $_modx->resource.id]}
<head>
    <title>{$_modx->getPlaceholder('mfilter.seo.title') ?: $_modx->resource.pagetitle}</title>
    <meta name="description" content="{$_modx->getPlaceholder('mfilter.seo.description') | esc}">

    {if $_modx->getPlaceholder('mfilter.seo.canonical')}
        <link rel="canonical" href="{$_modx->getPlaceholder('mfilter.seo.canonical') | esc}">
    {/if}

    {if $_modx->getPlaceholder('mfilter.seo.noindex')}
        <meta name="robots" content="noindex, follow">
    {/if}
</head>

Почему noindex, follow, а не noindex, nofollow

При AJAX-обновлении фильтров скрипт всегда ставит content="noindex, follow". Указывайте follow и в шаблоне, иначе разметка первой загрузки страницы разойдётся с состоянием после AJAX.

H1 и SEO-текст в теле страницы ​

Обязательные маркеры для AJAX

<h1> и контейнер SEO-текста нужно явно пометить атрибутом или классом — иначе JS не найдёт их при AJAX-фильтрации и они обновятся только после F5. У страницы может быть несколько <h1> (в шапке, в боковой колонке), поэтому просто «первый h1» не подходит.

  • <h1> — атрибут data-mfilter-h1 или класс mfilter-h1
  • SEO-текст — атрибут data-mfilter-seo-text или класс mfilter-seo-text
html
<h1 data-mfilter-h1>{$_modx->getPlaceholder('mfilter.seo.h1') ?: $_modx->resource.pagetitle}</h1>

<div data-mfilter-seo-text{if !$_modx->getPlaceholder('mfilter.seo.text')} style="display:none"{/if}>
    {$_modx->getPlaceholder('mfilter.seo.text')}
</div>

display:none нужен, чтобы пустой контейнер не мелькал до первой фильтрации. Дальше скрипт сам скрывает его при пустом SEO-тексте и показывает при заполненном.

Хлебные крошки и сброс фильтров ​

Хлебные крошки с выбранными фильтрами выводит сниппет mFilterCrumbs — для фильтров в SEO-адресе; после фильтрации без перезагрузки крошки не обновляются. Кнопку «Сбросить фильтры» делайте с атрибутом data-mfilter-reset. Скрипт показывает её при выбранных фильтрах и прячет без них, в том числе после фильтрации без перезагрузки, — см. mFilterForm.

Сортировка и лимит в своей разметке ​

Код ставьте ниже вызова mFilter:

html
{var $sort = $_modx->getPlaceholder('mfilter.sort')}
<select data-mfilter-sort>
    <option value="pagetitle-asc" {if $sort == 'pagetitle-asc'}selected{/if}>А-Я</option>
    <option value="price-asc" {if $sort == 'price-asc'}selected{/if}>Сначала дешевле</option>
</select>

Сортировку можно вывести и кнопками — значение указывается прямо в атрибуте:

html
<button data-mfilter-sort="price-asc">Сначала дешёвые</button>
<button data-mfilter-sort="price-desc">Сначала дорогие</button>
<a href="#" data-mfilter-sort="pagetitle-asc">По названию</a>

Скрипт обрабатывает <select> по событию change, а <button> и <a> — по клику, с отменой перехода. При загрузке страницы класс active текущей кнопке ставит вёрстка, дальше его переключает скрипт. Лимит data-mfilter-limit работает так же.

Переменные внутри чанков ​

Чанк сниппета получает данные как переменные Fenom; они же лежат в его $_pls. Глобальных плейсхолдеров среди них нет.

Чанки mFilter (карточки товаров) ​

tplOuter (обёртка результатов):

ПеременнаяОписание
$rowsHTML всех карточек товаров
$paginationHTML пагинации
$totalКоличество найденных товаров
$pageТекущая страница
$pageCountВсего страниц
$limitТоваров на странице
$hashХеш конфигурации вызова; выводится в data-mfilter-hash
$currentSortТекущая сортировка в формате поле-направление (price-asc) — для подсветки активного пункта в списке или кнопках
$currentTplТекущий вид из &tpls (tpl1, tpl2) — для подсветки переключателя сетка/список
$filterKeysКлючи фильтров страницы через запятую; выводится в data-mfilter-keys
$filtersПрименённые фильтры массивом, в исходном виде: на SEO-адресе диапазон приходит двумя ключами (price|min, price|max). Свёрнутый набор есть у формы — $appliedFilters

{$filters | print} на адресе …/vendor_id--acer/price--5000--40000/:

Array
(
    [vendor_id] => Array
        (
            [0] => 16
        )

    [price|min] => Array
        (
            [0] => 5000
        )

    [price|max] => Array
        (
            [0] => 40000
        )

)

После фильтрации без перезагрузки tplOuter не перерисовывается — заново приходят только строки и пагинация. Переменные tplEmpty — на странице mFilter.

Карточка одного товара (tpl, tpl1, tpl2 и другие) получает:

  • с &element — переменные элемента, например msProducts;
  • без &element — поля id, pagetitle, longtitle, description, introtext, uri, alias, а с MiniShop3 ещё price, old_price, thumb, vendor_id, article. TV в карточку не приходят.
html
<div class="product-card" data-id="{$id}">
    <img src="{$thumb}" alt="{$pagetitle | esc}">
    <h3>{$pagetitle | esc}</h3>
    <div class="price">{$price | number:0} ₽</div>
</div>

Чанки mFilterForm (форма фильтров) ​

Переменные обёртки формы, блока фильтра, значения и диапазона — на странице mFilterForm.

Чанки mFilterSelected (блок «Выбрано») ​

Переменные чанков блока «Выбрано» — на странице mFilterSelected.

Использование в JavaScript ​

Состояние фильтра, методы и события — на странице JS API. SEO-данные после фильтрации приходят в событии mfilter:success в e.detail.response.seo.