Přeskočit obsah
V
Pro vývojáře
Architektura, konvence, jádro systému a bezpečnost
Jádro / Filtry

Filtry#

Skladatelné podmínky nad QueryBuilderem. app/Core/Utils/Filters/.

Je to jediný způsob, jak se v tomhle systému dotazuje. Presenter ani komponenta SQL nepíší — poskládají pole filtrů a předají ho manageru.

$items = $this->newsManager->findAll(
    filters: [
        new EqualFilter('active', true),
        new LikeFilter('name', $hledany),
    ],
    orderBy: ['published' => false],   // false = DESC!
    limit: 20,
)->execute();

Katalog#

V systému je 33 filtrů (ls app/Core/Utils/Filters/*.php). Nejpoužívanější:

Filtr Co dělá
EqualFilter, NotEqualFilter rovnost / nerovnost
InFilter, NotInFilter hodnota (ne)patří do seznamu
LikeFilter, NotLikeFilter částečná shoda
IsNullFilter, IsNotNullFilter prázdná / neprázdná hodnota
GreaterThanFilter, GreaterOrEqualFilter, LessThanFilter, LessOrEqualFilter porovnání
BetweenFilter, NotBetweenFilter, RangeFilter, RangeInclusiveFilter rozsahy
DateEqualFilter, DateBeforeFilter, DateAfterFilter, DateBetweenFilter, DateRangeFilter datumové varianty
FulltextFilter fulltext nad databází
SearchIdFilter id vrácená z Elasticsearch
DistanceFilter geografická vzdálenost
ParameterRelFilter, ParameterRelLikeFilter, ParameterRelRangeFilter dynamické parametry inzerce a katalogu firem

Skládání a závorky#

Filtr Význam
AndFilter všechny podmínky
OrFilter kterákoli podmínka
GroupFilter závorka

Bez GroupFilter se OR rozlije do celého dotazu

A AND (B OR C) se bez závorky změní na A AND B OR C — a to je v SQL něco jiného, protože AND váže silněji. Dotaz nespadne, jen vrátí víc řádků, než měl.

Tečkové cesty přes vazby#

new EqualFilter('category.active', true)
new LikeFilter('translations.name', $hledany)

Potřebné joiny doplní JoinHelper sám a drží si aliasy per QueryBuilder — což je podstatné, protože jeden dotaz má vedle sebe QueryBuildery víc: hlavní, samostatný pro computeTotalCount() a klony, které si dělá GroupFilter pro každý podfiltr.

Tečková cesta přes KOLEKCI znásobí řádky

Filtr přes ToMany vazbu udělá join, který jeden záznam vrátí vícekrát. U výpisu s počtem to zkreslí čísla — stránkování pak ukazuje „137 položek“ tam, kde jich je padesát. Nespadne to a na první stránce to vypadá správně.

Pořadí filtrů se přerovnává#

FilterSortHelper::sortFilters() před sestavením dotazu přeuspořádá filtry podle pořadí sloupců v entitě a filtr nad víc poli rozbalí na jednotlivé. Volá se z GetAllTrait i z TotalCountTrait, aby oba dotazy vypadaly stejně.

Není to řazení výsledků

Řazení výsledků řeší orderBy a OrderByHelper — jiná věc, jiný soubor.

Řazení výsledků#

orderBy: ['published' => false, 'id' => true]   // published DESC, id ASC

Směr je BOOL, ne řetězec

OrderByHelper dělá doslova $dir = $direction ? 'ASC' : 'DESC'. Takže ['id' => 'DESC'] seřadí VZESTUPNĚ, protože neprázdný řetězec je truthy. Nikde to nespadne a výpis vypadá seřazeně — jen obráceně, takže „posledních deset“ ukáže deset nejstarších.

(V DataGridu je to jinak: setDefaultOrder() bere řetězec a převede si ho sám.)

Postup: píšu vlastní filtr#

  1. Nejdřív projdi katalog. Většina potřeb je pokrytá; nový filtr má smysl u domény, ne u další varianty porovnání.
  2. Implementuj App\Core\Utils\Filters\Interfaces\FiltertoSql(), getBindings(), apply() a settery aliasů.
  3. Vyjdi z nejbližšího existujícího filtru ve stejné složce; tvar je ustálený.
  4. Když filtr nesmí do cache, označ ho NonCacheableFilter.
  5. Ověř ho i v součtu. Filtr se aplikuje na hlavní dotaz i na dotaz počítající celkový počet — když se ty dva rozejdou, stránkování ukazuje nesmysly.

NonCacheableFilter je marker pro dotazy, které se nesmí cachovat

Používá ho SearchIdFilter: seznam id z Elasticsearch by v klíči cache vyrobil unikátní záznam pro každý hledaný výraz (a zaplnil Redis), a hlavně by vznikla druhá pravda o seznamu vedle ES indexu. Podrobně Cache.

Filtry v administraci a na frontu#

Kde Jak vzniknou
DataGrid z filtrů ve sloupcích + addDefaultFilter()
front výpisy z parametrů adresy přes filtrovací komponentu
fasety e-shopu vlastní pravidla — počty se zužují vším kromě vlastní fasety

Rozsahový filtr na frontu potřebuje round-trip přes adresu

Bez něj se hodnota mezi požadavky ztratí a filtr tiše nefiltruje — vrátí všechno a tváří se, že je nastavený.

Kam sáhnout#

Chci Kde
katalog filtrů app/Core/Utils/Filters/
rozhraní filtru app/Core/Utils/Filters/Interfaces/Filter.php
marker „necachovat“ …/Interfaces/NonCacheableFilter.php
doplňování joinů app/Core/Utils/Helpers/Filters/JoinHelper.php
přerovnání filtrů app/Core/Utils/Helpers/Filters/FilterSortHelper.php
směr řazení app/Core/Utils/Helpers/Sqls/OrderByHelper.php

Navazující kapitoly: Práce s tabulkami · Cache · Elasticsearch · Volání API