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

mFilter 1.5.0

Главное

Индивидуальные SEO-страницы для подборок. Собственные title, h1, description и текст для конкретной комбинации фильтров — например, только для страницы одного цвета. Раньше это требовало стороннего плагина со своей таблицей.

Новая вкладка в админке — SEO-страницы.

Новые возможности

Индивидуальные SEO для подборок

Заполненное поле перекрывает SEO-шаблон, незаполненное — оставляет его значение. Каждое поле проходит цепочку «индивидуальная запись → шаблон → значение по умолчанию» самостоятельно. Чтобы поменять только заголовок, достаточно заполнить один H1: при правке шаблона такие страницы обновятся вместе со всеми.

Записи создаются по требованию. Страница с одним фильтром и сорока значениями даёт сорок адресов, и заводить под них пустые строки значило бы засорить базу. Список подборок строится на лету из фильтров страницы; строка попадает в базу при первом сохранении, а очистка всех полей её удаляет.

Запись привязана к значению фильтра, а не к URL. Переименование слага, смена mfilter.url_separator или режима mfilter.slug_parent_prefix меняют адрес страницы, но запись продолжает работать.

Список перечисляет подборки по одному значению одного фильтра. Диапазоны и сочетания нескольких значений не перечисляются: между границами диапазона адресов бесконечно много, а сочетания растут мультипликативно.

Запись снимает noindex, навешенный mfilter.seo_max_filters или mfilter.seo_max_values, и восстанавливает canonical на саму отфильтрованную страницу. Настройка mfilter.seo_noindex_filtered сильнее в любом случае: если владелец закрыл все фильтрованные страницы, текст в поле этого не отменяет.

Если значение исчезло из каталога, запись не удаляется — помечается в списке и остаётся.

Чанк формы знает о состоянии фильтров

В tplOuter сниппета mFilterForm появились $hasFilters, $activeFiltersCount и $appliedFilters — кнопку сброса можно показывать только когда что-то выбрано:

html
<button type="reset" class="btn" data-mfilter-reset{if !$hasFilters} hidden{/if}>
    Сбросить (<span data-mfilter-reset-count>{$activeFiltersCount}</span>)
</button>

Штатного способа раньше не было: плейсхолдер mfilter.activeCount выставляется после рендера чанка, а mfilter.filters заполняется только при разборе SEO-URL. Новые переменные собираются из обоих источников.

Внимание

Кнопку нужно рендерить всегда и скрывать атрибутом hidden. Обёрнутую в {if $hasFilters} кнопку JS не найдёт в DOM и показать не сможет.

Счётчик считает диапазон за один фильтр: price|min и price|max схлопываются в price.

Разрешение конфликтов слагов

Две категории с одинаковым названием в разных ветках давали один и тот же слаг. Второй INSERT падал на уникальном индексе, запись не создавалась, и в URL подставлялся числовой ID вместо человекочитаемого адреса.

Конфликт теперь разрешается до записи. Поведением управляет новая настройка mfilter.slug_parent_prefix:

РежимПоведение
on_conflict (по умолчанию)Обычный слаг; при конфликте добавляется родитель — topki-axis. Существующие URL не меняются
alwaysРодитель добавляется всегда — pechi-axis. Меняет существующие URL, потребуются 301-редиректы
neverТолько числовой суффикс — axis-2

Префикс родителя доступен там, где значение фильтра — ID ресурса. Раньше это решалось двумя захардкоженными списками ключей, которые разъехались с реальными типами фильтров. Теперь тип сообщает о себе сам — новый метод FilterTypeInterface::getValueKind():

Вид значенияСлаг для числовогоПрефикс родителяТипы
resourceдадаparents, ms3_categories
entityданетvendors
scalar (по умолчанию)нетнетвсе остальные

Для сторонних типов ничего не ломается: scalar объявлен в AbstractFilterType.

Прочее

  • Понятная ошибка при ручном вводе занятого слага: 409 с текстом «Слаг «axis» уже занят значением «124»» вместо безликой ошибки 500.
  • Фильтр по источнику и сортировка по колонке «Источник» во вкладке «Алиасы».
  • Параметр &searchVar сниппета mFilter задаёт имя параметра запроса с поисковой фразой — раньше было жёстко mse_query.
  • Кнопку сброса формы можно помечать атрибутом data-mfilter-reset наравне с классом mfilter-reset-btn.

Исправления

SEO

  • SEO-шаблон не срабатывал на страницах с диапазоном. Шаблон подбирается по ключам фильтров, а SEO-адрес отдаёт диапазон двумя записями — price|min и price|max. Шаблон, настроенный на price, не совпадал ни с одной. Границы теперь сворачиваются к базовому ключу перед подбором.
  • Диапазон в заголовке читается по-человечески: «10600 — 15500», «от 10600», «до 15500» — через новые лексиконы mfl_seo_range_from, mfl_seo_range_to, mfl_seo_range_separator.
  • Приоритет SEO-шаблонов не работал — сортировка шла по несуществующему полю. Теперь по sort_order, где меньше значит выше.
  • Поле «Шаблон текста» в SEO-шаблонах не сохранялось: оно отсутствовало в карте полей модели xPDO, а xPDO молча отбрасывает поле, которого не знает.
  • Переустановка пакета ломала все SEO-шаблоны — колонка добавлялась только при обновлении, а таблицы при удалении компонента сохраняются намеренно.
  • SEO-текст не очищался при снятии фильтров, а на пустой выдаче AJAX-ответ не нёс ни SEO, ни адресов — на странице оставались заголовок и текст предыдущей подборки.
  • Неразобранный сегмент URL влиял на SEO: служебный ключ _hidden доходил до заголовка, участвовал в подсчёте лимитов индексации и попадал в canonical.

Фильтрация и вывод

  • С параметром element каталог рендерился пустым. mFilter безусловно передавал элементу свои значения showHidden/showUnpublished/showDeleted, перекрывая его собственные. Флаги теперь пробрасываются только когда заданы явно.
  • Сортировка каталога при AJAX работала только внутри страницы — элемент не получал текущую сортировку и отдавал ID в порядке по умолчанию.
  • При AJAX сбрасывались чанки, лимит и scope в native-режиме. Оба режима теперь используют общее хранилище конфига вызова.
  • data-total и data-page-count протухали после AJAX — атрибуты обёртки синхронизируются на каждом обновлении.
  • Дублирование фильтров в URL и в блоке «Выбрано» для мультизначных фильтров. Заодно utm_* и gclid больше не попадают в состояние фильтров.
  • Warmup мог вернуть пустой каталог: параметры авто-конфигов не обновлялись при расхождении. Пустой результат прогрева больше не кэшируется.

Блок «Выбрано»

  • Разметка сбрасывалась на дефолтные чанки при фильтрации. SelectedFilters.js содержал копию четырёх стандартных чанков и пересобирал блок из неё при каждом AJAX-ответе, поэтому кастомная разметка жила до первого клика по фильтру.

    Дублирование убрано: блок рендерится на сервере теми же чанками, что и при первой загрузке. Обратная совместимость полная — если в tplOuter нет атрибута data-mfilter-selected-hash, работает прежний клиентский рендер:

    html
    <div class="mfilter-selected" data-mfilter-selected
         {if $hash} data-mfilter-selected-hash="{$hash}"{/if}>
  • Кнопка «Сбросить всё» не работала на странице, отфильтрованной только диапазоном.

  • В блок при AJAX попадали посторонние ключи, включая служебный _hidden.

  • {$count} расходился с числом применённых фильтров.

  • Подписи значений для категорий MS3 и производителей показывали сырой ID.

Внимание при обновлении

Заголовки страниц с диапазоном изменятся

Страницы, отфильтрованные по диапазону, раньше не подбирали SEO-шаблон и генерировали заголовок по умолчанию. Теперь шаблон, настроенный на базовый ключ (price), к ним применяется — h1, title и description таких страниц изменятся, в том числе у уже проиндексированных.

Формат ключа кэша baseIds изменился

Ключ стал учитывать сортировку. После обновления первый запрос каждой сортировки пересчитает выборку.


mFilter 1.5.1

Патч к 1.5.0. Все исправления касаются переменных и настроек, которые documented-поведением обещаны, но до места назначения не доходили.

Исправления

  • Переменные, заявленные в документации, не доходили до чанков. Четыре штуки, все одного вида: значение читалось из источника, которого не существует, и молча подменялось запасным.

    ЧанкПеременнаяЧто выводилось
    tplSlider$labelключ фильтра вместо названия из поля «Заголовок»
    tplSlider$prefix, $suffixпусто при любых настройках
    tplReset («Выбрано»)$urlпусто — переменная не передавалась вовсе
    tplOuter («Выбрано»)$totalпусто — передавался только $count
  • $hex в чанке цветового свотча ставился не всегда — теперь приходит всегда, пустой строкой.

  • $filters в tplOuter сниппета mFilter существовал только в native-режиме.

  • Ссылки пагинации вели в 404 на сайтах с нестандартным разделителем. Чанк mfilter.pagination собирал сегмент как page_2, игнорируя mfilter.url_separator. Изнутри сайта дефект незаметен: JS перехватывает клик и строит адрес сам, минуя роутер. В 404 попадали при прямом открытии — из закладки, новой вкладки или поиска.

  • Настройка mfilter.default_limit не влияла на размер страницы. Её читали роутер, UrlBuilder и native-режим, но не сниппет — там было зашито 20. Теперь настройка задаёт значение по умолчанию для &limit, а переданный &limit её перекрывает. Значение доводится и до AJAX, иначе первая отрисовка и первый клик по фильтру дали бы разные размеры страницы.

  • Селект сортировки и переключатель вида в mfilter.outer всегда показывали значения по умолчанию — чанк читал $_pls['mfilter.sort'] и $_pls['mfilter.tpl'], а этих ключей в данных чанка нет. Сниппет теперь передаёт $currentSort и $currentTpl.

  • &pageParam не доходил до чанка пагинации в element-режиме.

Внимание при обновлении

Чанки не перезаписываются

Стандартные чанки при обновлении компонента не затираются — правки в mfilter.pagination и mfilter.outer дойдут только до новых установок. Сайтам со своей копией их нужно обновить вручную, иначе:

  • в mfilter.pagination останется зашитый page_, и ссылки пагинации продолжат отдавать 404 при прямом открытии;
  • в mfilter.outer останется собственное {var $currentSort = $_pls['mfilter.sort'] ?: 'pagetitle-asc'}, которое перекроет переданную переменную своим же значением по умолчанию — молча, без ошибок.

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

Не изменение поведения, а причина, по которой разметка, перенесённая из чанка в шаблон ресурса, ломается. Подробности — в Плейсхолдерах.

Не дублируйте обёртку data-mfilter-results в шаблоне. Получатся два вложенных контейнера, JS привяжется к внешнему, а data-mfilter-hash в нём будет пуст. AJAX без хэша не находит сохранённую конфигурацию вызова и уходит в native-режим — в выдачу попадают категории и прочие ресурсы, которых не было при первой загрузке.

Подписи над слайдерами изменятся

Там, где в чанке слайдера стоит {$label}, вместо ключа фильтра появится его название — price сменится на «Цена».

Отдельно про $hex: он теперь приходит всегда, пустой строкой вместо отсутствия. Чанк с проверкой {if isset($hex)} пойдёт по истинной ветке в любом случае — проверять надо {if $hex}.