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, SelectLikeFilter, PresetSelectFilter |
Filters/ |
filtr v hlavičce sloupce |
23 rendererů (BoolRenderer, PriceRenderer, QuantityRenderer, 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()). Upravit dostanesetAlwaysVisible(), ostatní akce skončí v nabídce „⋯“ (viz Akce na řádku):
$grid->addRowAction(
RowAction::create('edit')->setIcon('edit')->setVariant('success')
->setAlwaysVisible()
->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(). Vykreslí se v panelu „Hromadné akce“ pod tabulkou. - Š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.
Akce na řádku#
Šablona Parts/_row_actions.latte rozdělí akce řádku do tří skupin:
| Akce | Kde se vykreslí |
|---|---|
s RowAction::setAlwaysVisible() |
přímo v řádku jako tlačítko s popiskem |
| ostatní | v rozbalovací nabídce „⋯“ na konci řádku |
nebezpečné (varianta danger nebo ikona cross) |
vždy na konci nabídky, oddělené čarou a červeně, bez ohledu na setAlwaysVisible() |
Stromový grid přidává do nabídky ještě „Ve stromu“ (plochý pohled) a „Přesunout“. Nabídka
je Bootstrap dropdown (klávesnice, Escape, klik mimo) se strategií fixed, takže ji
vodorovně posuvný obal tabulky neořízne. Po AJAX překreslení řádku funguje dál.
Konvence: akce Upravit dostává ->setAlwaysVisible(), mají ji tak všechny výpisy
v administraci. Další akci nechte v řádku jen tehdy, když ji uživatel potřebuje
u většiny řádků.
Bez setAlwaysVisible() zmizí Upravit z řádku
Akce se nevytratí, jen se schová do „⋯“. Výpis vypadá funkčně, ale hlavní akce je o klik dál a v řádku zůstane jen nabídka.
Mazání do řádku nedostanete
setAlwaysVisible() na akci s variantou danger nebo ikonou cross nemá účinek.
Je to záměr: smazání nesmí být jeden klik vedle editace.
Hromadné akce#
Hromadné akce (GroupAction) se vykreslí v trvale viditelném panelu Hromadné akce
pod tabulkou (Parts/_foot.latte). Panel ukazuje stav výběru a vedle akcí gridu má dvě
tlačítka:
| Tlačítko | Atribut | Co dělá |
|---|---|---|
| Označit zobrazené | data-grid-select-page |
zaškrtne všechny řádky aktuální stránky |
| Zrušit výběr | data-grid-select-none |
odškrtne vše |
Obsluhuje je datagrid.js (delegovaně, takže přežije AJAX překreslení) přes kořen panelu
[data-grid-bulk]. Bez výběru jsou akční tlačítka vypnutá, „Označit zobrazené“ se vypne,
až je označeno všechno. Texty stavu výběru jsou v NEONu (data-text-none, data-text-some
s %count%), ne v JS.
Vypnuté tlačítko není kontrola
Bez JavaScriptu zůstanou akce aktivní. Obsluha hromadné akce proto dál musí sama ověřit, že dostala neprázdné pole id, a server výběr ověřuje jako dřív.
Styl filtrů#
Jak se filtry a řazení vykreslí, určuje nastavení administrace admin.dataGrid.filterStyle
(modul 1, upravuje se v Nastavení administrace vedle počtu položek na stránku). Čte ho
DataGrid::getFilterStyle() ze SettingContext, takže nestojí dotaz navíc.
| Hodnota | Konstanta | Vzhled |
|---|---|---|
style_1 (výchozí) |
FILTER_STYLE_COLUMNS |
řádek filtrů pod nadpisy sloupců, šipky řazení v nadpisu |
style_2 |
FILTER_STYLE_PANEL |
samostatný panel „Vyhledávání a řazení“ nad tabulkou, po načtení zavřený |
style_3 |
FILTER_STYLE_HEADER |
řádek filtrů pod nadpisy, řazení klikem na nadpis (další klik obrátí směr) |
Kořen gridu nese data-filter-style="style_…", podle něj se dá stylovat. Chybějící nebo
neznámá hodnota a grid v režimu Pickeru dají style_1. Mění se jen vykreslení: jména
polí filtrů, submit search, persistentní parametry i signály jsou ve všech stylech
stejné, presenter se kvůli stylu nemění. Klíč zakládá migrace
sql/2026-10-29_25_admin_datagrid_filter_style.sql.
Vlastní šablona části gridu v tématu#
Patičku a akce řádku si admin téma může nahradit bez kopie celého gridu. DataGrid::render()
hledá obě části přes BaseComponent::resolveThemeTemplatePath() a předá cestu do šablony:
| Část | Proměnná v šabloně | Výchozí soubor |
|---|---|---|
| akce řádku | $rowActionsTemplate |
Templates/Default/Parts/_row_actions.latte |
| patička a panel hromadných akcí | $footTemplate |
Templates/Default/Parts/_foot.latte |
Obě admin témata dnes používají výchozí části; vlastní verze tématu by ležela v
Templates/<Téma>/default/Parts/. Pořadí hledání popisuje Komponenty.
Přepsaná část musí zachovat jména a atributy
Tlačítka hromadných akcí musí zůstat <button n:name="'group__' . …"> (formulář gridu
je čte podle jména) a panel potřebuje data-grid-bulk, data-grid-select-page
a data-grid-select-none, jinak datagrid.js výběr neobslouží. Akce řádku musí
dál volat buildRowActionHref() a POST akce odesílat formulářem gridu.
Šíř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ší. Množství (stav skladu, rezervace, šarže) vykresluj
QuantityRenderer::create(): „1,5“ místo 1.500, „5“ místo 5.000, s oddělovačem lokalizace;
export CSV dostane strojový tvar s tečkou (1.5).
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.
Odkaz z buňky#
setLink() na sloupci obalí celý obsah buňky odkazem, s jakýmkoli rendererem. Parametry
odkazu se mapují z vlastností řádku (URL parametr => cesta k vlastnosti):
| Volání | Co udělá |
|---|---|
setLink(null, …) |
odkaz vypne; hodí se pro podmínku podle práva |
$requiredParams vynechaný (null) |
odkaz vznikne, jen když jsou vyplněné všechny namapované parametry |
$requiredParams = ['id'] |
povinné jsou jen vyjmenované parametry |
$requiredParams = [] |
odkaz vznikne vždy, i s prázdnými parametry |
Výchozí chování chrání před pádem link() na řádku, kterému chybí id: buňka se vypíše
jako text.
Na sloupec, jehož renderer sám kreslí <a>, setLink() nedávejte
EmailRenderer, LinkRenderer nebo přepínač by skončily jako odkaz v odkazu. To je
neplatné HTML, prohlížeč ho rozdělí po svém a kliknutí vede jinam, než čekáte.
Víc položek v jedné buňce#
| Renderer | Hodnota sloupce | Odkud data |
|---|---|---|
LinkListRenderer |
pole entit; text, tooltip a parametry odkazu se berou z každé položky | dávkově v setRowDecorator() do #[Transient] vlastnosti řádku, vzor app/UI/Admin/Eshop/Presenters/Traits/Lists/OrderListTrait.php |
BadgeListRenderer |
štítky z callbacku nad celou entitou | mapa, kterou presenter načte předem; žádný dotaz na řádek |
Sloupec s jedním z nich se neřadí ani nefiltruje. Text štítku BadgeListRenderer se
nepřekládá, šablona ho jen escapuje (jde o data z databáze, např. název role).
Vlastnost pro LinkListRenderer musí vracet pole, ne Collection
ValueResolver zredukuje kolekci na konci cesty na její první prvek. Buňka pak
tiše ukáže jednu položku místo všech.
Filtry, které nejsou rovnost#
SelectFilter porovnává hodnotu sloupce s volbou. Čtyři jeho potomci řeší případy, kdy to
nestačí:
| Filtr | Kdy | Co postaví | Vzor |
|---|---|---|---|
MappedSelectFilter |
volba není hodnotou sloupce (vybírá se sekce, sloupec drží threadId) |
IN (…) z mapy volba => hodnoty sloupce v setValueMap() |
app/UI/Admin/Base/Presenters/CommentPresenter.php |
PrecomputedIdsFilter |
ano/ne nad agregací z jiné tabulky („pod minimem“ přes všechny sklady) | pole IN (:ids) z setIdsProvider(); provider se zavolá jen u aktivního filtru |
app/UI/Admin/Store/Presenters/StockItemPresenter.php |
SelectLikeFilter |
sloupec drží seznam hodnot v jednom poli (JSON pole příznaků) | LIKE na výskyt volby; projdou jen volby z setOptions() |
app/UI/Admin/System/Presenters/PaymentPresenter.php |
PresetSelectFilter |
volba je pojmenovaná podmínka: skupina hodnot nebo kombinace sloupců („Po splatnosti“ = nezaplaceno a splatnost před dneškem) | hotový Core filtr z mapy volba => filtr v setPresets(); sloupec filtru jen určí místo v hlavičce, jeho cesty se nepoužijí |
stav faktury app/UI/Admin/Invoicer/Presenters/InvoicePresenter.php, skupiny stavů plateb PaymentPresenter.php |
->setFilter(
PresetSelectFilter::create()
->setOptions($statusLabels) // paid / unpaid / overdue
->setPresets(InvoiceStatusChoice::buildPresets(new \DateTime('today'))) // volba => Core filtr
)
Hodnota přijde z požadavku (?grid-filters[status]=overdue), takže na předfiltrovaný výpis
jde odkázat. Tak odkazují widgety dashboardu i karty „Co čeká na vyřízení“.
Neznámá nebo prázdná volba nefiltruje. Podmínky drží jedno místo (InvoiceStatusChoice,
PaymentStatusBuckets), ze kterého čte filtr gridu, počty widgetu i jeho odkazy, takže
karta a výpis, který otevře, napočítají totéž.
MappedSelectFilter: volba s prázdným seznamem nevrátí nic
Prázdný seznam se filtruje jako IN (0). Je to schválně: „ukaž sekci, kde zatím nic
není“ má odpověď prázdný výpis, ne všechno. Volba, která v mapě chybí úplně, filtr
nepoužije.
PrecomputedIdsFilter potřebuje setTargetField() na skutečný sloupec
Sloupec, na kterém filtr sedí, obvykle ukazuje odvozenou hodnotu bez sloupce
v databázi. Bez setTargetField() míří IN právě na ni a výpis se zapnutým filtrem
nefunguje. Cílem je mapovaný sloupec: primární klíč řádku nebo jeho cizí klíč.
Prázdná množina id dá prázdný výpis, ne všechno.
Stromový výpis#
setTreeStructure() řekne gridu, že záznamy tvoří strom (nested set). Grid pak nabídne
v liště tři pohledy:
| Pohled | Co ukazuje | Jak se načítá |
|---|---|---|
| Seznam (výchozí) | plochý stránkovaný výpis se sloupcem „Cesta“ (předci řádku) a akcí „Ve stromu“ | filtry, řazení a stránkování v SQL, předci řádků stránky jedním dotazem |
| Strom | hierarchie s ▸/▾, šipkami mezi sourozenci a zaměřením na podstrom | po rodičích: kořen nebo zaměřený uzel a rozbalené uzly, bez stránkování |
| Setřídit | vnořený seznam s přetahováním | celý strom; jen s enableDragDrop() a právem edit |
Volba pohledu se pamatuje v session pro každý grid, výchozí změní
setTreeDefaultView(DataGrid::VIEW_TREE). Hledání nebo řazení podle sloupce přepne i pohled
Strom na plochý výpis, protože hierarchii rozbije.
| Stav stromu | Kde žije |
|---|---|
| rozbalené uzly | session, zvlášť pro presenter, komponentu a scope |
| zaměření na podstrom | persistentní parametr treeRoot, adresu jde poslat dál |
| první otevření | strom do DataGrid::TREE_FULL_LIMIT (500) uzlů celý rozbalený, větší jen kořeny; „Rozbalit vše“ nad limitem odmítne |
| děti jednoho rodiče | nejvýš setTreeChildrenLimit() (výchozí 500), zbytek jako řádek „… dalších N“ |
setTreeCollapsible(false) je strom vždy celý rozbalený, bez session a zaměření, pro malé
stromy přetahované napříč (menu). Když přeroste limit, chová se z pojistky jako sbalitelný
a zapíše varování do logu.
Přesun i mazání dělá modelová vrstva set-based SQL nad lft/rgt, grid jen zavolá
metodu manageru:
| Přesun | Zapnutí | Metoda manageru |
|---|---|---|
| šipky ↑/↓ | setTreeStructure() |
moveUp() / moveDown() |
| přetažení v pohledu Setřídit | enableDragDrop() |
moveSubtreeTo() s rodičem a sousedy, jak je klient viděl |
| jen pořadí sourozenců, bez změny rodiče | enableDragDrop(flat: true), vzor AbstractParameterPresenter |
moveSubtreeTo(), vnořené cíle neaktivní |
| „Přesunout do…“ (modál s výběrem rodiče a pozice) | enableDragDrop() a strom s pravou mezí rgt |
moveSubtreeTo(), nikdy save() entity |
| „Seřadit abecedně“ (přímé děti podle popisku v aktuálním jazyce) | totéž a manager s reorderChildren() |
reorderChildren() jednou operací |
| smazání | akce delete |
deleteNode() |
Přetažení i modál posílají rodiče a sousedy, jak je uživatel viděl. Změnil-li se strom
mezitím, přesun vrátí TREE_STALE a nezapíše nic; grid ukáže hlášku a obnoví výpis.
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.
Co se při přesunu děje v modelové vrstvě (zámek stromu, kódy TREE_STALE a TREE_LOCKED,
změna rodiče z formuláře), popisuje kapitola Stromy.
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.
Práva ve výpisu#
Grid si klíče práv odvozuje z připojeného presenteru: :Admin:<Modul>:<Presenter>:<akce>,
stejně jako brány presenteru. Ruční podmínka isAllowed() kolem addRowAction() není
potřeba; když tam je, nevadí.
| Co | Klíč | Bez práva |
|---|---|---|
tlačítko „Přidat“ z setAddLink('add') |
…:add |
tlačítko se nevykreslí |
řádková i hromadná akce delete, signál delete! |
…:del |
akce se nevykreslí, signál vrátí 403 |
| hromadná akce s vlastním callbackem | přípona z GroupAction::setPrivilege() |
tlačítko se nevykreslí, odeslání vrátí 403 |
| inline editace, přepnutí vazby, šipky, přetažení, „Přesunout do…“, „Seřadit abecedně“ | …:edit |
signál vrátí 403, pohled Setřídit se nenabídne |
| export CSV | vlastní nemá | platí brána čtení stránky |
Signál delete! vrátí 403 i tehdy, když grid akci delete vůbec nezaregistroval.
setAddLink() odvozuje klíč jen z relativní akce ('add'); u absolutní destinace
(':Admin:Jiny:Modul:add') tlačítko neskrývá. Vlastní klíč dejte třetím parametrem,
'' odvození vypne. Mimo admin větev (presenter, jehož jméno nezačíná Admin:) se klíč
odvodit nedá a grid nic neskrývá ani neblokuje.
Postup: hromadná akce, kterou nesmí každý#
- Akci zaregistrujte s příponou práva:
$grid->addGroupAction(
GroupAction::create('exportXml')
->setPrivilege('export')
->setCallback(fn(array $ids) => $this->exportXml($ids))
);
- Klíč
:Admin:<Modul>:<Presenter>:exportzaložte v tabulce práv a povolte ho roliadmin. - Ověřte s rolí bez práva: tlačítko chybí a ručně odeslaný formulář vrátí 403.
Obrazovka s mazáním nebo zápisem v gridu potřebuje klíč del a edit
Brány jsou fail-closed. Bez řádku …:del (a …:edit u inline editace a stromu)
povoleného roli admin nesmaže ani nepřepne nic ani administrátor. U del to hlídá
kontrola 1.7 v tests/Manual/Admin/privilege_enforcement_audit.php.
Zapisující signál gridu přijímá jen POST
inlineUpdate!, relationToggle!, delete!, moveUp!, moveDown!, dragDrop!
a sortChildren! mají #[Requires(methods: 'POST')]. Řádková akce se setSignal() je
proto tlačítko odesílané POSTem; čtecí signál to vypne setPost(false). GET odkaz by
v přihlášené relaci spustil robot nebo přednačtení prohlížeče. Hlídá to
GridWritingSignalsRequirePostTest.
setVisibleWhen() jen skrývá tlačítko
Podmínka pro jednotlivý řádek (fn(object $row): bool, vzor
app/UI/Admin/Invoicer/Presenters/InvoicePresenter.php) zrcadlí stav entity, brána
mazání ji neřeší. Zakázaný stav musí odmítnout manager v beforeDelete(), jinak řádek
smaže ručně poslaný signál.
Export do CSV#
enableCsvExport('zakazky-' . date('Y-m-d') . '.csv') přidá do lišty tlačítko exportu.
Export vezme aktuální filtry a řazení, ale všechny řádky bez stránkování, načtené
z manageru po dávkách 500. Sloupce jsou sloupce gridu bez akcí, text buňky vrací
CellRenderer::toCsvValue().
| Vlastnost souboru | Hodnota |
|---|---|
| oddělovač | středník |
| kódování | UTF-8 s BOM, jinak ho Excel v češtině otevře v jiném kódování |
| hlavičky odpovědi | PrivateDownloadHeaders, soubor nezůstane v cache |
Postup: zapínám export
- V továrně gridu zavolejte
enableCsvExport()se jménem souboru (vzorapp/UI/Admin/Company/Presenters/WorkListPresenter.php). - Vlastní renderer, jehož
prepare()nevrací klíčtext(kreslí HTML, přepínač, ikonu), přepíšetoCsvValue(), jinak v exportu bude surová hodnota. - Ověřte export se zapnutým filtrem: obsahuje jen filtrované řádky, i ty za první stránkou.
Vlastní export mimo CsvExportTrait musí volat CsvCellSanitizer
Buňku začínající =, +, -, @, tabulátorem nebo CR spustí Excel jako vzorec, i když
ji zadal anonymní návštěvník veřejného formuláře. CsvCellSanitizer před ni přidá
apostrof, a to na jediném místě těsně před fputcsv(), ne v rendererech: nový renderer
by na ochranu zapomněl. Proto mají apostrof i záporná čísla.
Hromadná akce může vrátit soubor ke stažení
processGroupAction() po callbacku volá redirect('this'), ale sendResponse()
v callbacku vyhodí AbortException dřív, takže stažení projde bez dalšího signálu.
Formulář gridu se odesílá bez AJAXu, odpověď jde rovnou do prohlížeče. Chybová větev
(flash a return) nechá redirect proběhnout normálně. Vzor:
CatalogExportDownloadTrait::sendCatalogExport() (CallbackResponse + readfile()).
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/, části v …/Templates/Default/Parts/ |
| akce řádku a nabídku „⋯“ | …/Actions/RowAction.php, …/Templates/Default/Parts/_row_actions.latte |
| panel hromadných akcí | …/Templates/Default/Parts/_foot.latte, www/themes/admin/*/desktop/assets/js/datagrid.js |
| brány práv a signály | …/Traits/HandleTrait.php, DataGrid::isActionAllowed() |
| export CSV | …/Traits/CsvExportTrait.php, …/Utils/CsvCellSanitizer.php |
| pohledy a stav stromu | …/Traits/TreeStateTrait.php, …/Utils/TreeRowsBuilder.php, TreePathBuilder.php |
| „Přesunout do…“ a seřazení dětí | …/Traits/TreeMoveTrait.php |
| 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