Справочник API
Все публичные классы и методы пакета — сигнатуры, параметры, возвращаемые значения и
ошибки. Перечисления FilterType, MGOperator и TextMatch вынесены на отдельные
страницы: Операторы и
Режимы совпадения.
MuiGrid
Точка входа библиотеки. Финальный класс без состояния — единственный публичный метод статический.
MuiGrid::wrap()
Накладывает фильтры, сортировку и пагинацию запроса на готовый репозиторий.
Метод выполняет четыре шага. Сначала разрешается режим текстового совпадения: если схема
оставлена в режиме TextMatch::Auto, он определяется по драйверу базы, с которой работает
репозиторий. Затем схема строит условие WHERE из модели фильтров и добавляет его к
репозиторию через andWhere() — базовый запрос при этом не затрагивается. Дальше из модели
сортировки строится ORDER BY и выставляется методом orderBy(). Наконец, страница и
общее число строк запрашиваются у пагинатора фреймворка, который выполняет два запроса:
SELECT … LIMIT … OFFSET … и отдельный COUNT.
Синтаксис
public static function wrap(
RepositoryViewInterface $repo,
MuiGridRequest $request,
GridSchema $schema,
?callable $mapper = null,
): MuiGridResponseПараметры
$repo — репозиторий с уже применёнными SELECT, JOIN и базовым WHERE. Метод изменяет
этот объект: добавляет условия, выставляет порядок и границы страницы. Если репозиторий
нужен после вызова, клонируйте его заранее.
$request — разобранный запрос таблицы: страница, размер страницы, модель сортировки и
модель фильтров. Обычно это наследник MuiGridRequest с вашими дополнительными полями.
$schema — белый список колонок и запасной порядок. Определяет, что из присланного
браузером имеет право попасть в запрос.
$mapper — необязательный преобразователь строк вида fn (object $row): mixed.
Применяется к строкам текущей страницы после выполнения запроса.
Возвращает
MuiGridResponse — объект с полями rowCount (всего строк под фильтром) и rows
(текущая страница).
Ошибки
MuiGridException (HTTP 400) — поле отсутствует в схеме, не включено для фильтрации или
оператор не разрешён для типа колонки.
Пример
$repo = ArticleRepository::instance('a')
->select('a.id, a.title, a.views')
->where(Qb::eq('a.is_published', true));
$response = MuiGrid::wrap($repo, $request, $schema, fn ($r) => ArticleRow::from($r));
return ResponseEntity::ok($response->toArray());Результат
{ "rowCount": 134, "rows": [ { "id": 12, "title": "Winter internals" } ] }GridSchema
Набор колонок и запасной ORDER BY. Объект без состояния запроса — постройте один раз и
переиспользуйте.
GridSchema::make()
Создаёт схему из перечня колонок.
Колонки индексируются по имени поля. Если одно имя объявлено дважды, побеждает последнее вхождение.
Синтаксис
public static function make(GridColumn ...$columns): selfПараметры
$columns — переменное число колонок. Объявляйте только те, которыми клиенту действительно
позволено пользоваться: колонка, не включённая ни для фильтрации, ни для сортировки, не
делает ничего.
Возвращает
GridSchema — новую схему.
Пример
$schema = GridSchema::make(
GridColumn::for('title', 'a.title')->filterable(FilterType::String)->sortable(),
GridColumn::for('views', 'a.views')->filterable(FilterType::Number)->sortable(),
);GridSchema::defaultOrder()
Задаёт ORDER BY, применяемый, когда в запросе нет пригодной сортировки.
Используется в двух случаях: модель сортировки пуста или в ней остались только неизвестные
и несортируемые поля. Без этого вызова запрос без сортировки уйдёт в базу вообще без
ORDER BY, и порядок строк станет неопределённым — а значит, страницы перестанут быть
стабильными.
Синтаксис
public function defaultOrder(string $order): selfПараметры
$order — готовое выражение ORDER BY без ключевого слова. Это ваш SQL, он не проверяется
и не экранируется. Включайте в него уникальный «хвост» (обычно первичный ключ), иначе
строки с одинаковыми значениями сортировки будут переставляться между страницами.
Возвращает
GridSchema — ту же схему, для цепочки вызовов.
Пример
GridSchema::make(/* … */)->defaultOrder('a.created_at DESC, a.id DESC');GridSchema::textMatch()
Фиксирует, как для всей схемы пишется регистронезависимое совпадение.
По умолчанию схема находится в режиме TextMatch::Auto, и MuiGrid::wrap() определяет
написание по драйверу репозитория. Явно заданный режим отменяет определение.
Синтаксис
public function textMatch(TextMatch $mode): selfПараметры
$mode — один из режимов TextMatch: Auto, ILike, Like или Lower.
Возвращает
GridSchema — ту же схему, для цепочки вызовов.
Пример
GridSchema::make(/* … */)->textMatch(TextMatch::Lower);GridSchema::textMatchMode()
Возвращает режим, заданный схемой, — возможно, всё ещё Auto.
Нужен, когда режим требуется узнать до того, как он разрешён: например, чтобы применить тот же диалект к доменному фильтру.
Синтаксис
public function textMatchMode(): TextMatchВозвращает
TextMatch — режим схемы без разрешения Auto.
GridSchema::buildWhere()
Строит условие WHERE из модели фильтров.
Каждый элемент модели ищется в схеме по имени поля; неизвестное или незаявленное для
фильтрации поле останавливает обработку исключением. Элементы, давшие пустое условие
(например, isAnyOf с пустым набором), пропускаются. Итог объединяется через AND или,
если logicOperator равен or, через OR в скобках.
Обычно этот метод вызывает MuiGrid::wrap(); напрямую он нужен, когда условие требуется
отдельно от пагинации.
Синтаксис
public function buildWhere(MGFilterModel $model, ?TextMatch $textMatch = null): QbПараметры
$model — модель фильтров из запроса.
$textMatch — разрешённый режим совпадения. null означает «взять режим схемы»; если тот
всё ещё Auto — репозитория для определения здесь нет, поэтому применяется портируемый
TextMatch::Lower.
Возвращает
Qb — условие, либо Qb::empty(), если ничего действующего не осталось.
Ошибки
MuiGridException (HTTP 400) — неизвестное поле, поле без разрешения на фильтрацию или
оператор вне набора FilterType.
Пример
$where = $schema->buildWhere($request->filterModel, TextMatch::ILike);
echo $where->getQuery();Результат
(a.title ILIKE :iqb0 AND a.views >= :iqb1)GridSchema::buildOrder()
Строит выражение ORDER BY из модели сортировки.
Поля обрабатываются в том порядке, в котором их прислал браузер. Неизвестное или
несортируемое поле молча пропускается — таблица регулярно присылает переходное состояние
сортировки, и падать на нём было бы враждебно к интерфейсу. Если пригодных полей не
осталось, возвращается defaultOrder().
Синтаксис
public function buildOrder(array $sortModel): stringПараметры
$sortModel — массив MGSortItem из запроса.
Возвращает
string — выражение ORDER BY без ключевого слова, либо пустую строку, если нет ни
пригодной сортировки, ни запасного порядка.
Пример
echo $schema->buildOrder([new MGSortItem('title', 'desc')]);Результат
lower(a.title) DESCGridColumn
Декларация одной колонки: имя поля в браузере, SQL-выражение и разрешённые операции. Строится текучим интерфейсом.
GridColumn::for()
Создаёт колонку, сопоставляя имя поля SQL-выражению.
Свежая колонка не умеет ничего — ни фильтроваться, ни сортироваться. Возможности включаются явно, отдельными вызовами.
Синтаксис
public static function for(string $field, string $sql): selfПараметры
$field — имя поля, которое присылает браузер. Должно совпадать со свойством field у
колонки MUI DataGrid.
$sql — SQL-выражение, которому это поле соответствует. Подставляется в запрос как есть,
поэтому обязано приходить из вашего кода: колонка (a.title), колонка присоединённой
таблицы (au.name) или любое выражение (coalesce(a.nick, a.name)).
Возвращает
GridColumn — новую колонку.
GridColumn::filterable()
Разрешает фильтрацию по колонке и задаёт тип её значений.
Тип работает шлюзом: оператор, не входящий в набор этого FilterType, отклоняется до того,
как дойдёт до SQL. Наборы перечислены в Операторах.
Синтаксис
public function filterable(FilterType $type): selfПараметры
$type — FilterType::String, Number, Boolean или Date. Выбирайте по тому, как
значение ведёт себя в SQL, а не по типу в PHP: колонка со статусом, сравниваемая только на
равенство, — это String, а метка времени — Date.
Возвращает
GridColumn — ту же колонку, для цепочки вызовов.
Пример
GridColumn::for('views', 'a.views')->filterable(FilterType::Number);
// принимает =, !=, >, >=, <, <=, isAnyOf, isEmpty, isNotEmpty
// отклоняет contains, startsWith, … → HTTP 400GridColumn::sortable()
Разрешает сортировку по колонке, необязательно — по другому выражению.
Выражение сортировки независимо от выражения фильтрации: колонка может искаться по
a.title, а упорядочиваться по lower(a.title).
Синтаксис
public function sortable(?string $sqlExpr = null): selfПараметры
$sqlExpr — необязательное выражение для ORDER BY. null означает «сортировать по
основному выражению колонки». Как и оно, это доверенный SQL: он не проверяется и не
экранируется, поэтому не собирайте его из данных запроса.
Возвращает
GridColumn — ту же колонку, для цепочки вызовов.
Пример
GridColumn::for('title', 'a.title')->filterable(FilterType::String)->sortable('lower(a.title)');GridColumn::textMatch()
Фиксирует режим текстового совпадения для этой колонки, отменяя режим схемы.
Нужен, когда одной колонке требуется другое поведение: например, свёртка регистра через
lower() для не-ASCII имени, тогда как остальной таблице хватает режима соединения.
Значение TextMatch::Auto означает «наследовать от схемы» — то же самое, что не вызывать
этот метод вовсе.
Синтаксис
public function textMatch(TextMatch $mode): selfПараметры
$mode — режим для этой колонки.
Возвращает
GridColumn — ту же колонку, для цепочки вызовов.
GridColumn::filterUsing()
Переопределяет сопоставление «оператор → SQL» для этой колонки.
Резолвер вызывается после проверки типа и до стандартного сопоставления. Возврат
Qb перехватывает обработку; возврат null возвращает управление стандартному поведению
для этого оператора — так пишутся только исключения.
Синтаксис
public function filterUsing(callable $resolver): selfПараметры
$resolver — вызываемое значение вида fn (MGFilterItem $item, TextMatch $mode): ?Qb.
Второй аргумент — уже разрешённый режим совпадения; объявлять его необязательно, резолверы
с одним параметром продолжают работать.
Возвращает
GridColumn — ту же колонку, для цепочки вызовов.
Пример
GridColumn::for('authorName', 'au.name')
->filterable(FilterType::String)
->filterUsing(fn (MGFilterItem $i): ?Qb => match ($i->operator) {
MGOperator::IS_EMPTY => Qb::isNull('a.author_id'),
MGOperator::IS_NOT_EMPTY => Qb::isNotNull('a.author_id'),
default => null,
});Прочие методы GridColumn
| Метод | Возвращает | Что делает |
|---|---|---|
isFilterable() |
bool |
объявлен ли filterable() |
isSortable() |
bool |
объявлен ли sortable() |
orderExpr() |
string |
выражение для ORDER BY — своё или основное |
resolveFilter(MGFilterItem $item, TextMatch $mode) |
?Qb |
строит условие для элемента фильтра; вызывается схемой |
Публичные свойства: $field и $sql — доступны только для чтения.
MuiGridRequest
Конверт запроса таблицы. Гидрируется слоем запросов Winter из тела JSON; парсить массивы вручную не нужно.
Синтаксис
class MuiGridRequest
{
public function __construct(
#[Min(0)] public readonly int $page = 0,
#[Min(1), Max(10000)] public readonly int $pageSize = 20,
#[ListOf(MGSortItem::class)] public readonly array $sortModel = [],
#[Valid] public readonly MGFilterModel $filterModel = new MGFilterModel(),
) {
}
public function offset(): int; // $page * $pageSize
}Свойства
$page — индекс страницы с нуля. Отрицательное значение отклоняется валидацией.
$pageSize — число строк на странице. Допустимо от 1 до 10 000; верхняя граница защищает
от запроса «отдай всё» под видом пагинации.
$sortModel — список MGSortItem, гидрируется через #[ListOf].
$filterModel — вложенный MGFilterModel, проверяется через #[Valid]. Свойство
не допускает null и имеет пустое значение по умолчанию: отсутствующий в теле
filterModel станет пустой моделью, а явный "filterModel": null — ошибкой 400.
Наследование
Параметры конструктора в PHP не наследуются, поэтому в дочернем классе конверт объявляется
заново и передаётся в parent::__construct(). Атрибуты #[ListOf] и #[Valid] на
переобъявленных параметрах обязательны — без них гидратор не построит вложенные типы.
Свойство filterModel объявляйте как MGFilterModel $filterModel = new MGFilterModel(),
а не ?MGFilterModel = null: передача null в неnullable-родителя даст TypeError.
Базовый класс помечает readonly каждое свойство по отдельности, а не весь класс — именно чтобы наследник мог нормализовать свои поля в теле конструктора.
Пример
class ArticleGridRequest extends MuiGridRequest
{
public function __construct(
#[Positive] public ?int $authorId = null,
int $page = 0,
int $pageSize = 20,
#[ListOf(MGSortItem::class)] array $sortModel = [],
#[Valid] MGFilterModel $filterModel = new MGFilterModel(),
) {
parent::__construct($page, $pageSize, $sortModel, $filterModel);
}
}MuiGridResponse
Ответ, который возвращает MuiGrid::wrap(). Финальный, readonly, реализует
JsonSerializable.
Синтаксис
final readonly class MuiGridResponse implements JsonSerializable
{
public int $rowCount; // всего строк под фильтром, без учёта страницы
public array $rows; // текущая страница, уже после маппера
public function toArray(): array;
}Свойства
$rowCount — общее число строк, подходящих под фильтр. Именно это значение нужно
пагинатору таблицы; размер страницы здесь ни при чём.
$rows — строки текущей страницы. Если в wrap() передавался маппер, они уже преобразованы.
Пример
return ResponseEntity::ok($response->toArray());
// или, поскольку класс JsonSerializable:
return ResponseEntity::ok($response);Результат
{ "rowCount": 134, "rows": [ /* … */ ] }DTO запроса
Три небольших readonly-класса, из которых состоит запрос. Обычно вы встречаете их только
внутри резолвера filterUsing().
MGSortItem
Одна инструкция сортировки — { field, sort }.
readonly class MGSortItem
{
public string $field; // имя поля из браузера
public string $sort; // 'asc' | 'desc' — проверяется через #[In]
public function isDesc(): bool;
}Направление проверяется на этапе гидрации и принимается в любом регистре; отсутствующее направление трактуется как по возрастанию.
MGFilterItem
Одна инструкция фильтра — { field, operator, value }.
readonly class MGFilterItem
{
public string $field; // имя поля из браузера
public MGOperator $operator; // enum, приводится из строки запроса
public mixed $value; // значение как прислал клиент
}operator приводится к перечислению прямо при гидрации: неизвестная строка оператора
отклоняется ещё до того, как запрос доберётся до схемы.
MGFilterModel
Модель фильтров — { logicOperator, items }.
readonly class MGFilterModel
{
/** @var MGFilterItem[] */
public array $items; // #[ListOf(MGFilterItem::class)]
public string $logicOperator; // 'and' | 'or' — проверяется через #[In], по умолчанию 'and'
public function isOr(): bool;
}Пустой items не добавляет к запросу ничего — ваш базовый WHERE остаётся нетронутым.
MuiGridException
Исключение библиотеки. Наследует RequestException ядра, поэтому роутер отвечает 400
и отдаёт сообщение клиенту.
class MuiGridException extends RequestException {}Бросается в трёх случаях: поля нет в схеме, поле объявлено, но не включено для фильтрации,
оператор не входит в набор FilterType колонки. Его же удобно бросать из собственного
резолвера, когда значение фильтра не годится.
Пример
{ "message": "Operator 'contains' is not allowed for field 'views'" }Что дальше
- Операторы —
MGOperator,FilterTypeи матрица соответствий. - Режимы совпадения — перечисление
TextMatch. - Обновление до 3.0 — что изменилось в сигнатурах.