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

Модели и база данных ​

Схема актуальна для версий 1.5.0+.

Соглашения

  • Префикс таблиц — mfl_. Полное имя зависит от table_prefix MODX ({prefix}_mfl_*).
  • Поля даты-времени называются created_at / updated_at, как правило, типа datetime. У служебных таблиц запросов (mfl_request_runs, mfl_request_ids) created_at — число секунд UNIX.
  • Поля сортировки — sort_order или priority: слово rank в MySQL 8 зарезервировано.
  • JSON хранится в полях типа text с phptype json — xPDO сериализует его сам.

Полный список таблиц ​

ТаблицаМодельНазначение
mfl_filter_setsMflFilterSetНаборы фильтров
mfl_filter_set_resourcesMflFilterSetResourceПривязки наборов к ресурсам
mfl_slugsMflSlugSEO-алиасы значений фильтров
mfl_patternsMflPatternПаттерны URL-сегментов
mfl_seo_templatesMflSeoTemplateШаблоны title/h1/description и SEO-текста
mfl_seo_pagesMflSeoPageИндивидуальные SEO подборок (1.5.0+)
mfl_word_formsMflWordFormСловоформы для склонения значений
mfl_tv_indexMflTvIndexДенормализованный индекс TV-значений
mfl_facet_index_textMflFacetIndexTextИндекс текстовых значений фильтров (1.4.0+)
mfl_facet_index_numMflFacetIndexNumИндекс числовых значений фильтров (1.4.0+)
mfl_cacheMflCacheКэш промежуточных результатов и служебные записи
mfl_request_runsMflRequestRunЗапросы с большими списками ID (1.4.0+)
mfl_request_idsMflRequestIdsСписки ID: JOIN вместо IN(...) (1.4.0+)
mfl_warmup_configsMflWarmupConfigКонфигурации прогрева кэша (устарела с 1.4.0)
mfl_warmup_config_resourcesMflWarmupConfigResourceПривязки конфигураций прогрева к страницам (устарела)

Сплошная линия — ссылка на ID записи, пунктир — одно и то же значение в полях:


Конфигурация фильтров ​

MflFilterSet ​

Набор фильтров. К ресурсам привязывается через MflFilterSetResource.

ПолеТипОписание
idint unsigned PKID набора
namevarchar(255)Название
descriptiontext NULLОписание
filterstext (JSON)Конфигурация фильтров
inherittinyint(1) default 1Наследовать привязку на дочерние ресурсы
sort_orderint default 0Порядок сортировки в админке
activetinyint(1) default 1Активен
created_atdatetime NULL—
updated_atdatetime NULL—

Индексы: PRIMARY(id), idx_active(active), idx_sort_order(sort_order).

Структура filters (JSON):

json
{
  "vendor_id": {
    "type": "vendors",
    "source": "product",
    "label": "Производитель",
    "enabled": true
  },
  "color": {
    "type": "default",
    "source": "option",
    "field": "color",
    "label": "Цвет",
    "enabled": true
  },
  "price": {
    "type": "number",
    "source": "product",
    "field": "price",
    "label": "Цена",
    "enabled": true
  }
}

Значения type: default, number, boolean, vendors, colors, parents, ms3_categories, date, month, year, day и любые типы, зарегистрированные на OnMFilterInit. Описание типов — в разделе Типы фильтров.

Значения source: product (msProductData), option (msProductOption), tv, resource (поле modResource). См. Свой тип фильтра.

Пример:

php
use MFilter\Model\MflFilterSet;

// Получить набор
$filterSet = $modx->getObject(MflFilterSet::class, 1);
$filters = $filterSet->get('filters'); // уже массив (xPDO json)

// Создать набор и привязать его к ресурсу 5
$result = $mfilter->getFilterSetManager()->save([
    'name' => 'Каталог',
    'filters' => ['color' => ['type' => 'default', 'source' => 'option', 'field' => 'color']],
    'resource_ids' => [5],
]);
// ['success' => true, 'id' => 2, 'message' => 'Filter set created']

save() принимает ключи id (для обновления), name, description, filters, inherit, sort_order, active, resource_ids.

Сохранение набора из кода

  • Передавайте набор целиком. При обновлении отсутствующий ключ получает значение по умолчанию: filters станет пустым, name — пустой строкой.
  • Для новых ключей перестройте индекс фасетов. save() сбрасывает карту наборов и кэш значений страниц набора, но не индекс фасетов: админка перестраивает его отдельно. Вызовите $mfilter->getFacetIndexBuilder()->rebuildKeys(['color']). Иначе ключ попадёт в индекс только с товарами, сохранёнными позже. До полной пересборки индекса фильтры default, colors и number покажут значения лишь этих товаров.
  • Пишете моделями напрямую (newObject()->save()) — сразу вызовите $mfilter->getFilterSetManager()->clearCache(). Без этого новые привязки и смена inherit не видны ни getForResource(), ни сайту. Кэш значений страниц clearCache() не трогает — его сбрасывает $mfilter->invalidatePageCache($resourceId).

MflFilterSetResource ​

Привязка набора к ресурсу. Один ресурс может быть привязан только к одному набору (uk_resource).

ПолеТипОписание
idint unsigned PKID привязки
filter_set_idint unsignedID набора
resource_idint unsignedID ресурса
created_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_resource(resource_id), idx_filter_set(filter_set_id).

Подсказка

Наследование на дочерние ресурсы задаёт флаг inherit самого набора, а не отдельная таблица. Заранее оно раскладывается в карту core/cache/mfilter/filter_sets.cache.php (ресурс → набор); карту сбрасывают сохранение и удаление набора. Новые и перенесённые разделы карта не видит до сброса — см. Пересобрать кэш.

php
$manager = $mfilter->getFilterSetManager();

// Привязать набор 1 ещё и к ресурсу 5
$set = $manager->getById(1); // вместе с resource_ids
$set['resource_ids'][] = 5;
$manager->save($set);

// Найти набор для ресурса — с учётом наследования
$filterSet = $manager->getForResource($resourceId); // массив или null

resource_ids заменяет все привязки набора. Ресурс из списка, привязанный к другому набору, переходит к этому. Без resource_ids привязки не меняются.


SEO ​

MflSlug ​

SEO-алиас значения фильтра.

ПолеТипОписание
idint unsigned PK—
filter_keyvarchar(100)Ключ фильтра (color, vendor_id, ...)
valuevarchar(255)Оригинальное значение
slugvarchar(255)SEO-алиас
sourcevarchar(50)Источник: option, product, tv, resource — значения опций, полей и TV; vendor, parent, ms3_category — производители и разделы; url_builder — алиас, созданный при сборке адреса; manual — добавлен вручную во вкладке «Алиасы»
culture_keyvarchar(20) NULLЯзыковой ключ
is_customtinyint(1) default 0Алиас отредактирован вручную: автоматически не пересоздаётся
activetinyint(1) default 1—
created_atdatetime NULL—
updated_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_filter_value_culture(filter_key, value, culture_key), UNIQUE uk_filter_slug_culture(filter_key, slug, culture_key), idx_slug(slug), idx_source(source), idx_culture(culture_key).

php
// Найти активный slug; нет записи для 'ru' — берётся запись без языка
$slug = $mfilter->getSlugManager()->findSlug('color', 'Красный', 'ru'); // 'krasnyj' или null

// Создать (используйте SlugManager — он гарантирует уникальность)
$slug = $mfilter->getSlugManager()->getOrCreate('color', 'Красный', 'option', 'ru');

У производителей и разделов в value хранится id, а не название: findSlug('vendor_id', '3', 'ru').

MflPattern ​

Паттерн URL-сегмента одного фильтра: вид сегмента в адресе вместо стандартного ключ--значение — см. Паттерны URL.

ПолеТипОписание
idint unsigned PK—
filter_keyvarchar(100)Ключ фильтра
url_patternvarchar(255)Шаблон URL-сегмента (dvigatel--{value}, {value}...)
hide_keytinyint(1) default 0Сегмент содержит только значение, без префикса ключа
parse_regexvarchar(255) NULLРегулярное выражение для разбора. Пустое — строится автоматически из url_pattern
parse_typevarchar(50) default defaultТип парсинга (default, range...)
priorityint default 100Приоритет применения паттерна (меньше = раньше)
activetinyint(1) default 1—

Индексы: PRIMARY(id), UNIQUE uk_filter_key(filter_key), idx_priority(priority).

Подсказка

Обычно parse_regex оставляют пустым: с версии 1.3.3 выражение строится из url_pattern. Для паттернов со скрытым ключом оно не строится. Вручную заполняйте поле только для нестандартных адресов.

MflSeoTemplate ​

SEO-шаблоны title, H1, description и SEO-текста для страниц с выбранными фильтрами.

ПолеТипОписание
idint unsigned PK—
namevarchar(255)Название (для админа)
page_idint unsigned NULLID конкретного ресурса, без дочерних. NULL = шаблон применяется ко всем
filter_keystext (JSON)Массив ключей фильтров, к которым применим шаблон
h1_templatevarchar(500) NULLШаблон H1
title_templatevarchar(500) NULLШаблон title
description_templatetext NULLШаблон meta description
text_templatetext NULLШаблон SEO-текста (1.5.0+)
sort_orderint default 0Приоритет (меньше = раньше)
activetinyint(1) default 1—
culture_keyvarchar(20) NULLЯзыковой ключ
created_atdatetime NULL—
updated_atdatetime NULL—

Индексы: PRIMARY(id), idx_page(page_id), idx_sort_order(sort_order), idx_culture(culture_key).

Структура filter_keys:

json
["vendor_id", "color"]

Порядок выбора шаблона: сначала точное совпадение ключей, затем частичное — хотя бы один общий ключ. Шаблон конкретной страницы важнее общего. Подробнее: SEO шаблоны.

MflSeoPage ​

Индивидуальные SEO: title, H1, description и текст для одной подборки. С версии 1.5.0. Строка появляется, когда запись впервые сохраняют в админке. Подробнее: Индивидуальные SEO.

ПолеТипОписание
idint unsigned PK—
resource_idint unsignedID страницы каталога
filter_signaturevarchar(40)SHA-1 от отсортированных значений фильтров подборки, а не от адреса
filter_datatext (JSON)Те же фильтры в читаемом виде: {"color": ["Красный"]}
h1varchar(500) NULLH1
titlevarchar(500) NULLTitle
descriptiontext NULLMeta description
texttext NULLSEO-текст
activetinyint(1) default 1—
culture_keyvarchar(20) default ''Языковой ключ. Админка пишет, а сайт читает только пустую строку — запись без языка
created_atdatetime NULL—
updated_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_resource_signature_culture(resource_id, filter_signature, culture_key), idx_resource(resource_id).

filter_signature для своей записи считает $mfilter->getSeoPageManager()->signature($filters).

MflWordForm ​

Словоформы для склонения значений в SEO-текстах: 12 падежных форм и формы «где», «куда», «откуда».

ПолеТипОписание
idint unsigned PK—
filter_keyvarchar(100)Ключ фильтра
valuevarchar(255)Оригинальное значение
nomvarchar(255) NULLИменительный (кто? что?)
genvarchar(255) NULLРодительный (кого? чего?)
datvarchar(255) NULLДательный (кому? чему?)
accvarchar(255) NULLВинительный (кого? что?)
insvarchar(255) NULLТворительный (кем? чем?)
prevarchar(255) NULLПредложный (о ком? о чём?)
nom_pl ... pre_plvarchar(255) NULLТе же 6 форм во множественном числе
where_formvarchar(255) NULLФорма «где?» (в Москве)
to_formvarchar(255) NULLФорма «куда?» (в Москву)
from_formvarchar(255) NULLФорма «откуда?» (из Москвы)
culture_keyvarchar(20) NULLЯзыковой ключ
is_autotinyint(1) default 0Сгенерировано автоматически (через morpher.ru)
created_atdatetime NULL—
updated_atdatetime NULL—

WordFormsManager::save() принимает формы множественного числа под ключами nom_plural … pre_plural и сам раскладывает их по колонкам *_pl.

Индексы: PRIMARY(id), UNIQUE uk_filter_value_culture(filter_key, value, culture_key), idx_filter(filter_key), idx_culture(culture_key).

Подробнее: Словоформы.


Индексы ​

MflTvIndex ​

Денормализованный индекс TV-значений. По нему считаются счётчики значений TV-фильтров — без JOIN к modTemplateVarResource.

ПолеТипОписание
idint unsigned PK—
resource_idint unsignedID ресурса
tv_idint unsignedID TV
tv_namevarchar(100)Имя TV (денормализовано для скорости)
valuevarchar(255)Значение TV
labelvarchar(255) NULLПодпись значения (для TV типа listbox)
created_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_resource_tv_value(resource_id, tv_id, value), idx_tv_value(tv_id, value), idx_tv_name_value(tv_name, value), idx_resource(resource_id).

Заполняется автоматически на OnDocFormSave (если mfilter.tv_index_on_save = true) и при ручной переиндексации.

MflFacetIndexText ​

Денормализованный индекс текстовых значений фильтров: производители, цвета, размеры, made_in и т. п. С версии 1.4.0.

ПолеТипОписание
filter_keyvarchar(64)Ключ фильтра (color, made_in, ...)
filter_valuevarchar(255)Значение фильтра (как в источнике)
product_idint unsignedID товара

Индексы:

  • PRIMARY(filter_key, filter_value, product_id) — покрывающий: обеспечивает уникальность и быстрое чтение по ключу фильтра или по ключу со значением;
  • idx_product(product_id) — для удаления строк при пересборке отдельных товаров.

Внимание

Строки двух товаров — по строке на каждое значение фильтра:

filter_keyfilter_valueproduct_id
f_styleklassika8
f_stylemodern9

Не используйте getCollection/getObject напрямую — таблица может содержать миллионы строк. Только через FacetIndexReader.

MflFacetIndexNum ​

Аналогично MflFacetIndexText, но для числовых фильтров (price, weight, height...).

filter_keyfilter_valueproduct_id
price65217.00008
price38323.00009
ПолеТипОписание
filter_keyvarchar(64)Ключ фильтра
filter_valuedecimal(20,4)Числовое значение
product_idint unsignedID товара

Индексы: PRIMARY(filter_key, filter_value, product_id), idx_product(product_id).

php
$range = $mfilter->getFacetIndexReader()->getNumRange('price', $productIds);
// ['min' => 1990.0, 'max' => 89990.0, 'count' => 1234]

Кэш и служебные таблицы ​

MflCache ​

Кэш промежуточных результатов фильтрации: счётчики значений, ID отфильтрованных товаров, baseIds прогрева. Кроме кэша в таблице лежат служебные записи: параметры вызова сниппетов (snippet_config_*, срок 30 дней, продлевается при обращении) и метки индекса фасетов (facet_index.*).

ПолеТипОписание
idint unsigned PK—
cache_keyvarchar(255)Ключ (формат: {prefix}_{resourceId}_{hash}; служебные — snippet_config_{hash}, facet_index.*)
cache_valuemediumtextСериализованные данные (JSON); у меток facet_index.* — число секунд UNIX
created_atdatetime NULL—
expires_atdatetimeВремя истечения

Индексы: PRIMARY(id), UNIQUE uk_cache_key(cache_key), idx_expires(expires_at).

Не очищайте таблицу целиком

TRUNCATE или DELETE без условия стирают служебные записи. После этого фильтрация без перезагрузки на страницах из кэша MODX получает отказ, а синхронизация индекса фасетов пропускает правки товаров, сделанные с прошлого запуска, — до полной пересборки. Очищайте кнопкой «Очистить кэш» на вкладке «Обслуживание» или $mfilter->clearCache(): они служебные записи не трогают, кроме давно не запрошенных параметров вызова.

Подробнее: Кэширование.

MflRequestRun ​

Один запрос с большим списком ID. Через эту таблицу RequestIdsRegistry делает JOIN вместо WHERE id IN (...) с десятками тысяч ID.

ПолеТипОписание
idbigint unsigned PK auto_incrementrun_id, выдаётся атомарно
created_atint unsigned default 0Время создания, секунды UNIX

Индексы: PRIMARY(id), idx_created_at(created_at).

MflRequestIds ​

Сами списки ID, привязанные к run_id из MflRequestRun. Первичный ключ составной, отдельного id нет.

ПолеТипОписание
run_idbigint unsigned PKFK на MflRequestRun.id
product_idint unsigned PKID товара
created_atint unsigned default 0Секунды UNIX (для задачи очистки)

Индексы: PRIMARY(run_id, product_id), idx_created_at(created_at).

Кто удаляет строки:

  1. Деструктор RequestIdsRegistry — run_id и его строки в конце запроса.
  2. Обработчик завершения PHP (shutdown handler) — на случай фатальной ошибки.
  3. Scheduler-задача mfl_cleanup_request_ids каждые 10 минут — страховка от kill -9.

См. RequestIdsRegistry.


Прогрев (устарело) ​

Устарело с версии 1.4.0

Эти таблицы используются только для прогрева baseIds на страницах, где mFilter вызван с &element. После полной стабилизации индекса фасетов они будут удалены.

MflWarmupConfig ​

Конфигурация прогрева — один вызов сниппета (element и params), результат которого сохраняется в кэш.

ПолеТипОписание
idint unsigned PK—
namevarchar(255)Название
elementvarchar(100)Имя сниппета (msProducts, pdoResources...)
snippet_calltext NULLПолный вызов сниппета (для отладки)
paramstext (JSON)Параметры вызова
cache_key_hashvarchar(32)MD5 от элемента и параметров отбора, отличных от значений по умолчанию (уникальность)
activetinyint(1) default 1—
auto_createdtinyint(1) default 0Создан автоматически из реального вызова mFilter
last_warmup_atdatetime NULLВремя последнего прогрева
last_duration_msint NULLДлительность последнего прогрева
last_product_countint NULLКоличество найденных товаров
created_atdatetime NULL—
updated_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_cache_key_hash(cache_key_hash), idx_active(active).

MflWarmupConfigResource ​

Привязка конфигурации прогрева к страницам каталога.

ПолеТипОписание
idint unsigned PK—
config_idint unsignedFK на MflWarmupConfig.id
resource_idint unsignedID ресурса
created_atdatetime NULL—

Индексы: PRIMARY(id), UNIQUE uk_config_resource(config_id, resource_id), idx_config(config_id).


Миграции ​

Таблицы создаются сами при установке и обновлении пакета — резолвером _build/resolvers/resolve.tables.php. createObjectContainer() создаёт только недостающие таблицы, поэтому колонки в существующие добавляет _build/resolvers/resolve.migrations.php. При удалении пакета таблицы остаются.

Программное создание таблиц ​

php
$manager = $modx->getManager();

// Создать конкретную таблицу
$manager->createObjectContainer(\MFilter\Model\MflFilterSet::class);

// Добавить индекс вручную (если меняли metaMap)
$manager->addIndex(\MFilter\Model\MflSlug::class, 'idx_slug');

Изменения схемы между версиями ​

ВерсияЧто изменилось
1.5.0Добавлены mfl_seo_pages и колонка mfl_seo_templates.text_template
1.4.0Добавлены mfl_facet_index_text, mfl_facet_index_num, mfl_request_runs, mfl_request_ids
1.2.0Добавлены mfl_warmup_configs, mfl_warmup_config_resources
1.1.0Удалена mfl_page_configs (заменена на mfl_filter_sets + mfl_filter_set_resources); поля rank/createdon переименованы в sort_order/created_at для совместимости с MySQL 8