Práce s tabulkami#
DataGrid je komponenta výpisu v administraci: sloupce, filtry, řazení, stránkování,
akce na řádku, hromadné akce a volitelně strom s přesouváním. Kód:
app/UI/Admin/System/Components/DataGridComponent/.
Zdrojem dat je manager, ne dotaz — grid si sám poskládá filtry, řazení i stránkování
a předá je do findAll().
Z čeho se grid skládá#
| Prvek | Složka | K čemu |
|---|---|---|
Column, MultiColumn, RelationColumn |
Columns/ |
jeden sloupec, víc hodnot v jednom sloupci, sloupec nad vazbou |
RowAction, GroupAction |
Actions/ |
akce na řádku, hromadná akce nad zaškrtnutými |
TextFilter, SelectFilter, MultiSelectFilter, RangeFilter, MappedSelectFilter, PrecomputedIdsFilter |
Filters/ |
filtr v hlavičce sloupce |
22 rendererů (BoolRenderer, PriceRenderer, ImageRenderer, ToggleRenderer, …) |
Renderers/ |
jak se hodnota vykreslí |
Postup: zakládám nový výpis#
- Továrnu gridu injektuj do presenteru (
DataGridFactory) a napišcreateComponentGrid(): DataGrid. - Zdroj a základ výpisu:
$grid = $this->dataGridFactory->create();
$grid->setDataSource($this->tagManager)
->setIncludes(['translations'])
->setItemsPerPage($this->getAdminItemsPerPage())
->setDefaultOrder('id', 'desc')
->setAddLink('add');
- Sloupce přes
Column::create(); k čemu se má filtrovat, dostanesetFilter():
$grid->addColumn(
Column::create('name')
->setLabel($t('columns.name'))
->setProperty('translations.name') // tečková cesta přes vazbu
->setSortable()
->setWidth(60)
->setFilter(TextFilter::create())
);
- Akce na řádku — buď odkaz (
setLink()), nebo signál (setSignal()):
$grid->addRowAction(
RowAction::create('edit')->setIcon('edit')->setVariant('success')
->setLink('edit', ['id' => 'id'])
);
$grid->addRowAction(
RowAction::create('delete')->setIcon('cross')->setVariant('danger')
->setSignal('delete')->setConfirm($t('confirmDelete'))
);
- Hromadné akce přes
GroupAction::create()— vlastní chování dostanesetCallback(). - Šablona obsahuje jen
{control grid}. - Prokliknij filtr, řazení, stránkování, obě akce a prázdný stav.
setIncludes() musí pokrývat všechno, co grid zobrazuje
Sloupec s tečkovou cestou (translations.name) potřebuje tu vazbu načtenou. Bez
ní se nevypíše chyba — jen prázdná buňka, což vypadá jako chybějící data.
Tečkové cesty joiny doplní samy, ale hloubka něco stojí
category.parent.name funguje, jen se to promítne do dotazu. U výpisu, který
zobrazuje stovky řádků, je levnější denormalizovaný sloupec.
Šířky sloupců#
setWidth(X) dá hlavičce třídu width-X. SCSS generuje pravidla jen pro násobky
pěti od 5 do 100 (@for $i from 1 through 20 { th.width-#{$i * 5} }).
Jiná hodnota než násobek pěti se tiše zahodí
setWidth(12) vyrobí třídu width-12, ke které žádné CSS pravidlo neexistuje —
sloupec zůstane bez nastavené šířky. Nespadne to a v kódu to vypadá správně; pozná
se to jen pohledem na rozvržení. (V kódu takové případy dnes jsou.)
Řazení#
setDefaultOrder() bere směr jako řetězec ('asc' / 'desc') a převede si ho sám.
První volání zahodí vestavěné id DESC, další volání přidávají další úroveň:
V surovém poli orderBy je směr BOOL, ne řetězec
Když řazení skládáte ručně pro findAll(), platí
['id' => true] → ASC, ['id' => false] → DESC. ['id' => 'DESC'] seřadí
VZESTUPNĚ, protože neprázdný řetězec je truthy. Nikde to nespadne, výpis vypadá
seřazeně — jen obráceně, takže „posledních deset“ ukáže deset nejstarších.
Bez zahození vestavěného id DESC by nastavení tiše nedělalo nic
Tak se to kdysi chovalo: setDefaultOrder('reports', 'desc') vyrobilo
ORDER BY id DESC, reports DESC a obrazovka nahlášených hodnocení neplnila svůj
hlavní účel. Dnes je to opravené, ale je dobré vědět, proč to tak je.
Vlastní obsah buňky#
Vykreslení se řeší rendererem, ne anonymní funkcí v definici sloupce. Renderery
jsou hotové pro většinu typů: BoolRenderer, DateRenderer, PriceRenderer,
ImageRenderer, EmailRenderer, PhoneRenderer, BadgeRenderer, ToggleRenderer,
LinkRenderer, PdfDownloadRenderer a další.
Pro odkazy z buňky existuje setLink() přímo na sloupci; pro úpravu celé stránky
výsledků naráz je setRowDecorator(), který se zavolá jednou po načtení.
Renderer se dá napsat vlastní
Stačí implementovat CellRenderer. Je to lepší než HTML v šabloně gridu — ten je
sdílený všemi výpisy v administraci.
Stromový výpis#
setTreeStructure() přepne grid na strom (nested set): výpis se řadí do hloubky,
buňka se odsazuje podle úrovně a řádky dostanou šipky pro přesun mezi sourozenci.
Přesun i mazání dělá modelová vrstva set-based SQL nad lft/rgt, grid jen zavolá
moveUp() / moveDown() / deleteNode().
setTreeScope(['menuId' => $id]) řeší víc stromů v jedné tabulce — přidá se do filtrů
výpisu i do přesunových dotazů, aby přepočet lft/rgt nezasáhl cizí strom.
Bez setTreeScope() přepočet zasáhne i cizí stromy v téže tabulce
Přesun položky v jednom menu přečísluje i ostatní menu. Neprojeví se to chybou — projeví se to rozsypaným pořadím jinde, než kde jste pracovali.
Smazání uzlu odstraní celý podstrom
A zacelí po něm mezeru v lft/rgt. V seznamu to vypadá jako smazání jednoho
řádku.
Přesun na kořen stromu je nejrizikovější operace
Přepočítává se celá struktura. U velkého stromu to trvá a při přerušení zůstane strom nekonzistentní — a nekonzistentní nested set se projeví jako náhodně mizející větve, ne jako chyba.
Mazání#
Signál i hromadná akce volají na manageru metodu delete. setDeleteMethod('softDelete')
to přepne na jinou — e-shopové gridy to tak dělají.
Hromadná akce dostává POLE identifikátorů
Obsluha, která čeká jedno id, spadne. A protože je základ sdílený všemi výpisy v administraci, spadnou tím naráz všechny — ne jen ten, který jste upravovali.
Stav výpisu#
Grid si pamatuje stránku, řazení i filtry.
„Výpis nic neukazuje“ bývá zapamatovaný filtr z minula
Zkuste ho vyresetovat dřív, než začnete hledat chybu v kódu nebo v datech.
Režim výběru#
Grid se umí vykreslit jako obsah Pickeru — bez přidání, editace, mazání
a zaškrtávátek, s tlačítkem „Vybrat“ na řádku. Akci, která má být vidět i tam,
označte showInPickerMode().
Podrobně Admin presenter, oddíl o režimu Pickeru.
Kam sáhnout#
| Chci | Kde |
|---|---|
| API gridu | app/UI/Admin/System/Components/DataGridComponent/DataGrid.php |
| definici sloupce | …/Columns/BaseColumn.php, Column.php, MultiColumn.php |
| filtry | …/Filters/ |
| renderery | …/Renderers/ |
| šablonu gridu | …/Templates/Default/ |
| SQL pro strom | app/Core/Traits/Api/Models/Repositories/TreeTrait.php |
| směr řazení v SQL | app/Core/Utils/Helpers/Sqls/OrderByHelper.php |
| hotový vzor | app/UI/Admin/Blog/Presenters/TagPresenter.php |
Navazující kapitoly: Admin presenter · Picker · Filtry · Formuláře