MUI Data Grid
Winter MUI Data Grid — серверный адаптер для таблицы
MUI X DataGrid. Вы описываете схему колонок,
а библиотека принимает запрос таблицы (страница, модель сортировки, модель фильтров),
проверяет его по этой схеме как по белому списку, накладывает условия на ваш репозиторий
и возвращает { rowCount, rows } — ровно то, что таблица ждёт от сервера.
Проблема
MUI X DataGrid умеет работать в серверном режиме: вместо того чтобы держать всю таблицу в браузере, она отправляет на сервер своё состояние — какую страницу показать, по каким колонкам сортировать, какие фильтры применил пользователь. Выглядит это так:
{
"page": 0,
"pageSize": 25,
"sortModel": [{ "field": "views", "sort": "desc" }],
"filterModel": {
"logicOperator": "and",
"items": [{ "field": "title", "operator": "contains", "value": "winter" }]
}
}Дальше начинается ручная работа, которая в каждом проекте пишется заново и одинаково
неудачно: разобрать этот JSON, сопоставить field из браузера с колонкой в базе,
перевести operator в SQL, не забыть про LIMIT/OFFSET, посчитать общее число строк
отдельным COUNT. И главное — не дать клиенту отсортировать по колонке, которую он не
должен видеть, или подставить в фильтр то, что попадёт в запрос как есть.
Наивная реализация («возьмём field и подставим в ORDER BY») открывает SQL-инъекцию
через имя колонки. Осторожная — превращается в сотню строк ветвлений на каждый эндпоинт.
Решение
Библиотека делает эту работу один раз, а от вас требует одну декларацию — схему:
$schema = GridSchema::make(
GridColumn::for('title', 'a.title')->filterable(FilterType::String)->sortable(),
GridColumn::for('views', 'a.views')->filterable(FilterType::Number)->sortable(),
GridColumn::for('authorName', 'au.name')->filterable(FilterType::String)->sortable(),
)->defaultOrder('a.created_at DESC');
return MuiGrid::wrap($repo, $request, $schema);Схема — это контракт между таблицей в браузере и базой. В ней сказано: какие имена полей существуют, какому SQL-выражению каждое соответствует, что с ним разрешено делать (фильтровать, сортировать) и какого типа его значения. Всё, чего в схеме нет, до базы не доходит.
Философия
- Наложение, а не генерация. Библиотека не строит ваш запрос.
SELECT,JOINи бизнесовыйWHEREпишете вы; она только добавляетandWhere()для фильтров, выставляетorderBy()и отдаёт пагинацию штатномуPaginator. Поэтому всё, что выразимо в SQL — CTE, подзапросы, вычисляемые колонки — работает без оговорок. - Схема — белый список. Не «фильтруем всё, кроме запрещённого», а «фильтруем только то, что объявлено». Забыть закрыть колонку невозможно: колонка, которой нет в схеме, закрыта по умолчанию.
- Имена — из вашего кода, значения — из запроса. SQL-выражение колонки берётся из схемы и в шаблон запроса подставляется как есть; значение пользователя всегда уходит связанным параметром. Инъекция невозможна по построению.
- Диалект — свойство соединения, а не грида. Регистронезависимый поиск пишется по-разному в PostgreSQL, MySQL и SQLite. Библиотека определяет нужное написание по драйверу репозитория, так что одна и та же схема работает на любой из трёх баз.
Ключевые понятия
GridSchema— набор колонок плюс запаснойORDER BY. Единственный источник правды о том, что клиенту позволено.GridColumn— одна колонка: имя поля в браузере → SQL-выражение, плюс флаги «можно фильтровать» и «можно сортировать».FilterType— тип значения колонки (String,Number,Boolean,Date). Определяет, какие операторы колонка принимает: числовая колонка откажетcontains, строковая —>.MuiGridRequest— разобранный запрос таблицы. Гидрируется слоем запросов Winter, вручную ничего парсить не нужно.MuiGrid::wrap()— точка входа: берёт репозиторий, запрос и схему, возвращаетMuiGridResponse.TextMatch— как пишется регистронезависимое совпадение (ILIKE/LIKE/lower()). По умолчанию определяется автоматически.
Возможности
- Пагинация с общим счётчиком —
LIMIT/OFFSETиCOUNTодним вызовом, через штатныйPaginatorфреймворка. - Фильтры со шлюзом по типу — двадцать три оператора MUI, каждый разрешён только для
подходящего
FilterType. AND/OR— как вfilterModel.logicOperator.- Мультиколоночная сортировка — по модели сортировки, с запасным порядком, когда сортировки нет.
- Своё выражение сортировки — колонка может фильтроваться по
a.title, а сортироваться поlower(a.title). - Переопределение фильтра на колонку —
filterUsing()для случаев, где нужен свой SQL. - Маппер строк — превратить плоские строки в ресурсы, не выходя из вызова.
- Портируемость — PostgreSQL, MySQL, SQLite без изменений в коде.
Кому это подходит
Любому админ-интерфейсу или списку, где данных больше, чем разумно отдавать в браузер: таблица остаётся отзывчивой, потому что на клиент едет одна страница, а не весь набор.
Требования
- PHP ≥ 8.4
flytachi/winter-kernel^4.0 — слой запросов, который гидрирует и валидирует запрос таблицыflytachi/winter-ppa^1.0 — слой данных: репозитории, конструктор запросов и пагинатор
Оба пакета Composer поставит сам. Подробности — в Установке.
Установка
composer require flytachi/winter-mui-data-gridКак это выглядит
Схема объявлена, запрос пришёл — остаётся отдать ответ:
$repo = ArticleRepository::instance('a')
->select('a.id, a.title, a.views')
->where(Qb::eq('a.is_published', true));
return ResponseEntity::ok(
MuiGrid::wrap($repo, $request, $schema)->toArray()
);Ответ:
{ "rowCount": 134, "rows": [ { "id": 12, "title": "Winter internals", "views": 9001 } ] }Полный путь — от установки до работающей таблицы в браузере — в Быстром старте.
Исходники и ссылки
- GitHub — github.com/flytachi/winter-mui-data-grid
- Packagist — packagist.org/packages/flytachi/winter-mui-data-grid
- MUI X DataGrid — mui.com/x/react-data-grid
Продолжите с Установки, Быстрого старта и Ментальной модели.