K

Tabulka

Podřádky

Podřádky vykreslují související dětské záznamy v rozbalitelném panelu pod každým rodičovským řádkem. Použijte je, když uživatelé potřebují proniknout do detailu — položky faktury, zásilky objednávky, úkoly projektu — bez opuštění tabulky.

Podřádky preview
Na této stránce

Podřádky vykreslují související dětské záznamy v rozbalitelném panelu pod každým rodičovským řádkem. Použijte je, když uživatelé potřebují proniknout do detailu — položky faktury, zásilky objednávky, úkoly projektu — bez opuštění tabulky.

┌───┬────────────┬──────────────┬───────────┬──────────────┐
│ ▾ │ INV-1001 │ Northwind │ paid │ 9 350 Kč │ ← rodičovský řádek
│ └──────────────────────────────────────────────────────┐
│ Product Qty Unit Line total Actions│ ← dětská tabulka
│ 27" monitor 1 5 600 Kč 5 600 Kč [✎][🗑] │
│ Keyboard 2 1 200 Kč 2 400 Kč [✎][🗑] │
│ Wireless mouse 3 450 Kč 1 350 Kč [✎][🗑] │
│ Subtotal: 9 350 Kč │ ← součet per rodič
│ └──────────────────────────────────────────────────────┘
│ ▸ │ INV-1002 │ Globex │ pending │ 18 100 Kč │
└───┴────────────┴──────────────┴───────────┴──────────────┘

Základní nastavení

use NyonCode\WireTable\Columns\TextColumn;
use NyonCode\WireTable\Table;
 
public function table(Table $table): Table
{
return $table
->model(Invoice::class)
->columns([
TextColumn::make('number')->label('Invoice')->sortable(),
TextColumn::make('customer')->label('Customer'),
TextColumn::make('total')->money(),
])
->subRows('items') // metoda Eloquent relace
->subRowColumns([ // sloupce pro dětskou tabulku
TextColumn::make('product')->label('Product'),
TextColumn::make('quantity')->numeric()->label('Qty'),
TextColumn::make('unit_price')->money()->label('Unit'),
]);
}

subRows('items') očekává metodu relace (items()) na rodičovském modelu. Dětské sloupce jsou nezávislé na rodičovských — mohou to být úplně jiná pole.

Kdy je použít

Podřádky použijte, když:

  • rodičovský záznam vlastní malou sadu dětských záznamů,
  • uživatelé potřebují rychlý drill-down bez změn routy,
  • dětská data sdílejí stejný rozhodovací kontext jako rodičovský řádek.

Vyhněte se jim, když je dětská sada dost velká, aby si zasloužila vlastní tabulku s vlastními filtry a stránkováním — podřádky jsou detailní prostředek, ne druhý grid.

Rozbalení a sbalení

->subRowsExpandable() // uživatel může přepnout (výchozí true)
->subRowsDefaultExpanded() // začít rozbalené
->subRowsExpandable(false) // vždy otevřené, bez přepínače
->subRowsToggleLabel('Show items') // popisek sloupce přepínače

Rozbalení je jeden stav, ne dva: výchozí stav (baseline) říká, jestli řádky začínají otevřené, a Livewire stav drží jen řádky, které se od něj liší. subRowsDefaultExpanded() nastaví baseline; uživatel s ním pohne master chevronem v hlavičce sloupce s chevrony — ikonou přímo nad řádkovými chevrony:

┌───┬────────────┬──────────────┐
│ » │ FAKTURA │ CELKEM │ » master chevron: otevřít / zavřít vše
├───┼────────────┼──────────────┤
│ › │ INV-1001 │ 9 350 Kč │ › chevron řádku
│ › │ INV-1002 │ 18 100 Kč │
└───┴────────────┴──────────────┘

Tři cesty k němu, jeden stav za nimi:

Ovládání Kde
Master chevron Hlavička sloupce s chevrony
⌥/Alt-klik na chevron řádku Kterýkoli řádek — zkratka ze spreadsheetů
Rozbalit u všech řádků View menu (tlačítko ⊞); na mobilu jediné hromadné ovládání, protože rozložení karet nemá hlavičku

Protože baseline je režim, ne seznam klíčů, platí i pro řádky na stránkách, které uživatel ještě neotevřel. Se zapnutým rememberColumns() se volba ukládá per uživatel spolu s rozložením sloupců.

Řaditelné dětské řádky

Nechte uživatele řadit dětskou tabulku kliknutím na hlavičky sloupců, s volitelným výchozím řazením aplikovaným před jakoukoli interakcí:

->subRowsSortable(default: 'line_total', direction: 'desc')
Product ↕ Qty ↕ Unit ↕ Line total ▼ ← ▼ aktivní řazení, ↕ řaditelné
─────────────────────────────────────────
27" monitor 1 5 600 Kč 5 600 Kč
Keyboard 2 1 200 Kč 2 400 Kč
Wireless ... 3 450 Kč 1 350 Kč

Řadit lze jen sloupce přítomné v subRowColumns() — libovolné názvy sloupců jsou odmítnuty, takže je řazení bezpečné řídit z requestu. Kliknutí na aktivní sloupec otočí směr; kliknutí na jiný ho seřadí vzestupně. Aktivní řazení je sdílené napříč všemi rozbalenými rodiči.

Limit a „Zobrazit více"

->subRowsLimit(5)

Když je nastaven limit a existuje více dětí, na konci dětské tabulky se vykreslí tlačítko „Zobrazit N dalších". Kliknutí odhalí celou sadu pro daného rodiče (sledováno per rodič ve stavu), zatímco počet zůstává přesný.

Eager load načte jen limit řádků na rodiče (nativní per-parent eager-load limit) plus jeden count dotaz pro přesné součty — celé dětské sady se nikdy nenačítají do paměti, dokud není rodič rozbalen přes „Zobrazit více":

Product Qty Line total
───────────────────────────────
Keyboard 2 2 400 Kč
Wireless mouse 3 1 350 Kč
Show 1 more ← objeví se, protože limit (2) < total (3)

Filtrování dětského dotazu

Tvarujte podkladový dotaz relace pomocí subRowQuery():

use Illuminate\Database\Eloquent\Builder;
 
->subRowQuery(fn (Builder $query) => $query
->where('active', true)
->orderBy('sort_order')
)

Zapněte per-dítě interaktivní filtry pomocí subRowsFilterable(). Lišta filtrů se vykreslí nad dětskou tabulkou pro každý filtrovatelný sloupec podřádku a zúží děti — rodičovské řádky zůstanou nedotčené:

->subRowsFilterable()
Filter: [ Product… ] [ price from – to ] ✕ Reset
───────────────────────────────────────────────────────
Product Qty Line total
Keyboard 2 2 400 Kč ← jen řádky odpovídající filtru

Filtrování podle hodnot podřádků z filtrů tabulky

Označte jakýkoli filtr hlavní tabulky pomocí subRows() a míří na dětské záznamy místo rodičovských sloupců — např. filtr Měsíc/Rok nad datem dětí. Název sloupce filtru (zde billed_at) odkazuje na dětský model:

use NyonCode\WireTable\Filters\DateFilter;
 
->filters([
DateFilter::make('billed_at')->month()->subRows(),
])

Jeden filtr omezí vše konzistentně:

  • rodiče se zmenší na ty s aspoň jedním odpovídajícím dítětem,
  • rozbalené panely ukážou jen odpovídající děti,
  • mezisoučty per rodič, počty „zobrazit více", rollup sloupce (->sums(), ->counts(), …) a jejich celkové součty v patičce agregují jen odpovídající děti.
Měsíc: [ 2026-06 ▾ ]
┌───┬────────────┬──────────────┐
│ ▾ │ INV-1001 │ 5 000 Kč │ ← rollup počítá jen červnové položky
│ └── June item A … June item B ──┘
│ ▾ │ INV-1003 │ 5 000 Kč │ ← INV-1002 (jen květen) zmizela
└───┴────────────┴──────────────┘
│ Celkem: │ 10 000 Kč │ ← patička sčítá filtrované podřádky

Sestavení diagramu

Žádné extra zapojení není potřeba — agregáty stačí deklarovat a filtr je zúží automaticky. Součty lze získat dvěma způsoby podle toho, zda má být částka per faktura viditelným rodičovským sloupcem:

Částka jen v podřádcích. Dejte sloupci podřádku mezisoučet per rodič (scope: 'subRows') a souhrn ve výchozím rozsahu pro celkový součet — ten se vykreslí v hlavní patičce, spočtený v SQL nad přesně těmi dětmi, které filtr povoluje (viz Celkové součty ze sloupců podřádků):

$table
->subRows('items')
->subRowColumns([
TextColumn::make('product'),
TextColumn::make('line_total')
->suffix(' Kč')
->summaryDecimals(0)
->summarizeSum('Subtotal', scope: 'subRows') // patička panelu per faktura
->summarizeSum('Celkem'), // hlavní patička, filtrované děti
])
->filters([
DateFilter::make('billed_at')->month()->subRows(),
]);

Částka jako rodičovský sloupec. Použijte rollup sloupec nad stejnou relací jako subRows() — shoda relace je to, co filtru umožňuje ji omezit. Buňka pak ukazuje součet odpovídajících položek každé faktury (sloupec 5 000 Kč v diagramu) a její souhrn je celkový součet v patičce. Uložený rodičovský atribut jako $invoice->total by na filtr nereagoval — přesně to rollup nahrazuje:

$table
->columns([
TextColumn::make('number')->label('Invoice'),
TextColumn::make('items_total')
->label('Total')
->sums('items', 'line_total') // stejná relace jako ->subRows('items')
->suffix(' Kč')
->summaryDecimals(0)
->summarizeSum('Celkem'),
])
->subRows('items')
->filters([
DateFilter::make('billed_at')->month()->subRows(),
]);

Omezení rollupu je klíčované názvem relace: rollup nad jinou relací (řekněme ->counts('payments')) zůstane items-scopnutým filtrem nedotčen a dál agreguje všechny své děti.

Viz Filtry — Filtrování podle hodnot podřádků.

Řádkové akce

Vykreslete per-dítě akce v koncové buňce akcí. Každá akce se vykreslí proti dětskému záznamu, přesně jako akce hlavní tabulky proti rodiči:

use NyonCode\WireCore\Actions\Action;
use NyonCode\WireCore\Actions\DeleteAction;
 
->subRowActions([
Action::make('edit')->label('Edit')->icon('pencil')->color('primary'),
DeleteAction::make(),
])
Product Qty Line total Actions
─────────────────────────────────────────────
27" monitor 1 5 600 Kč [✎ Edit][🗑 Delete]
Keyboard 2 2 400 Kč [✎ Edit][🗑 Delete]

Mezisoučty per rodič

Dejte sloupci podřádku souhrn v rozsahu subRows a dětská tabulka získá patičku s tou agregací pro děti rodiče:

->subRowColumns([
TextColumn::make('product')->label('Product'),
TextColumn::make('quantity')->numeric()->summarizeSum(scope: 'subRows'),
TextColumn::make('line_total')
->numeric(0)
->suffix(' Kč')
->summaryDecimals(0)
->summarizeSum('Subtotal', scope: 'subRows'),
])
Product Qty Line total
───────────────────────────────
27" monitor 1 5 600 Kč
Keyboard 2 2 400 Kč
Wireless mouse 3 1 350 Kč
Subtotal: 6 9 350 Kč ← patička per rodič

Platí zde všechny typy agregací a formátování čísel ze stránky Souhrny. Pro součet napříč všemi rodiči v hlavní patičce přidejte druhý souhrn ve výchozím rozsahu na stejný sloupec podřádku — ->summarizeSum('Celkem') — nebo dejte rodičovskému rollup sloupci vlastní souhrn. Viz Celkové součty ze sloupců podřádků a Celkové součty přes všechny děti.

Začít plně rozbalené

Otevřít podřádky všech rodičů hned od prvního vykreslení — hodí se na kontrolu a procházení, kdy chcete vidět všechen detail pohromadě:

->subRowsDefaultExpanded()
┌───┬────────────┬──────────────┐
│ ⌄ │ INV-1001 │ 9 350 Kč │ rozbalené jsou všechny faktury,
│ └── Monitor … Klávesnice ───┘ ne jen ta, na kterou uživatel klikl
│ ⌄ │ INV-1002 │ 18 100 Kč │
│ └── Stůl … Židle … ─────────┘
└───┴────────────┴──────────────┘

Je to jen výchozí bod — master chevron a view menu tímto výchozím stavem za běhu pohnou na obě strany.

flattenSubRows() je zastaralý alias subRowsDefaultExpanded(). Navzdory názvu nikdy nevykreslil děti jako ploché řádky; byl to druhý příznak se stejným viditelným efektem a existence obou znamenala, že „Sbalit vše“ nedokázalo zavřít to, co flatten režim držel otevřené. toggleFlattenMode() dál funguje a volá toggleAllRowExpansion().

Detail-řádkový režim (bez relace)

Vynechte subRows() úplně a poskytněte jen dětský pohled: rozbalený panel pak vykreslí samotný rodičovský záznam — ideální pro detailní kartu. Podřádky se aktivují, jakmile je nastaven subRowView() (nebo subRowColumns()), i bez relace:

->subRowView('components.users.detail') // žádné volání subRows() → detail-řádkový režim
┌───┬────────────┬──────────┐
│ ▾ │ Alice │ Active │
│ └──────────────────────────────────┐
│ Email: alice@example.com │ ← váš vlastní Blade
│ Phone: +420 777 123 456 │
│ └──────────────────────────────────┘
└───┴────────────┴──────────┘

Vlastní pohled

Nahraďte výchozí renderer dětí úplně, když data nejsou přirozeně tabulková:

->subRowView('components.orders.sub-rows')

Pohled dostane $table, $component, $record (rodič), $subRows (kolekce dětí) a layoutové proměnné.

Výkon: Eager loading

Podřádky se načítají pro celou stránku v jednom dotazu místo jednoho dotazu na rozbaleného rodiče:

  • Vše rozbalené — děti každého rodiče se načtou najednou.
  • Jinak — načtou se jen aktuálně rozbalení rodiče.

To odstraňuje N+1, které by jinak rostlo s počtem otevřených řádků. Čtení dětí rodiče (a jeho počtu pro mezisoučet) pak nestojí žádné extra dotazy. Eager loading se automaticky přeskočí, když jsou aktivní interaktivní filtry podřádků, protože filtrování per rodič spadne zpět na bezpečný per-parent dotaz.

Reference voleb

Metoda Účel
subRows(string $relation) Zapnout podřádky z relace (vynechejte + nastavte subRowView/subRowColumns pro detail-řádkový režim)
subRowColumns(array $columns) Sloupce pro dětskou tabulku
subRowQuery(Closure $cb) Tvarovat dotaz dětské relace
subRowsSortable(bool, ?string $default, string $direction) Řazení klikem na hlavičky + výchozí řazení
subRowActions(array $actions) Řádkové akce per dítě
subRowsLimit(?int) Omezit děti, zapnout „Zobrazit N dalších"
subRowsFilterable(bool) Per-dítě interaktivní lišta filtrů
subRowsExpandable(bool) Povolit přepínač rozbalit/sbalit
subRowsDefaultExpanded(bool) Začít rozbalené
subRowsToggleLabel(?string) Popisek sloupce přepínače
flattenSubRows(bool) Zastaralý alias subRowsDefaultExpanded()
subRowView(string) Vlastní renderer dětí

Související dokumentace