Panels
Routování
Jak z deklarovaných stránek vzniknou skutečné URL — makro, tvar URL, middleware pro každý resource, několik zón nad jednou sadou resourců a cesta přes config.
Na této stránce
Registr nevlastní žádný URL shell ani routu: routy zůstávají aplikaci, v její
vlastní skupině, s jejím prefixem a middlewarem. Co framework odstraňuje, je
opakování — čtyři řádky Route::get() na každý resource a vedle nich ručně psaná
mapa klíč→URL pro menu.
Jak to funguje
Resource řekne, které stránky ho vykreslují — a stejně tak cokoli dalšího, co aplikace zaregistrovala, včetně dashboardu: router čte tentýž katalog jako menu, takže routovatelnost je věcí deklarace stránek, ne toho, jaký druh věci to je.
use NyonCode\WireCore\Foundation\Routing\Contracts\ProvidesPages;use NyonCode\WireCore\Foundation\Routing\RoutePage; public static function pages(): array { return [ 'index' => ListOrders::class, 'create' => CreateOrder::class, 'view' => ViewOrder::class, 'edit' => RoutePage::make(EditOrder::class)->permission('orders.update'), ];}
a aplikace je zaregistruje uvnitř své vlastní skupiny:
// routes/web.phpRoute::prefix('admin') ->middleware(['auth', 'verified']) ->domain(config('app.admin_domain')) ->group(function () { Route::wireResources(); Route::wireResource(OrderResource::class); // nebo po jednom });
Prefix, middleware i doména jsou vaše — jsou to obyčejné Laravelí routy registrované ve skupině, ve které jste macro zavolali. Resource, který nedeklaruje stránky, se přeskočí; tak zůstane interní nebo vnořený resource neroutovaný. Jmenovat takový resource explicitně naopak vyhodí výjimku, protože to je chyba, ne volba.
Tvar URL
| Druh stránky | URL | Jméno routy |
|---|---|---|
index |
{prefix} |
wire.{key}.index |
create |
{prefix}/create |
wire.{key}.create |
view |
{prefix}/{record} |
wire.{key}.view |
edit |
{prefix}/{record}/edit |
wire.{key}.edit |
| cokoli dalšího | {prefix}/{druh} |
wire.{key}.{druh} |
{prefix} je registrovaný klíč, takže klíč v menu a URL se shodují, aniž by se
kterýkoli z nich opakoval. {record} je klíč, ne navázaný model: stránky si
záznam resolvují samy, což nechává soft-delete scope, tenant guard i
non-Eloquent zdroj rozhodnutím stránky, ne routeru.
Druh stránky, který router nezná, sedí na vlastním jméně a může říct jinak přes
uri(). Ta URI zároveň rozhoduje, jestli je stránka o jednom záznamu:
{record} v ní znamená, že ho routa bere — a že je stránka záložkou v
sub-navigaci záznamu, ať se jmenuje jakkoli.
'history' => RoutePage::make(OrderHistory::class)->uri('{record}/history'), // stránka záznamu'archive' => RoutePage::make(ArchivedOrders::class), // stránka seznamu
Oprávnění, middleware a domény
RoutePage::permission() dosedne na routu jako Laravelí can: middleware. Nic
tady autorizaci neimplementuje znovu — odpovídá na ni Gate, přesně jako
u akcí, sloupců a widgetů, takže spatie/laravel-permission
i nyoncode/laravel-permission-extended fungují beze změny. Odmítnutí se stane
v routeru, dřív než se stránka vykreslí nebo padne dotaz.
Na úrovni resource přidává ConfiguresRoutes tři věci, které patří
jednomu resource a ne celé skupině:
public static function routeMiddleware(): array { return ['can:tenants.view']; }public static function routeDomain(): ?string { return '{tenant}.example.com'; }public static function routePrefix(): ?string { return 'billing/tenants'; }
Parametr domény se dostane do vašeho TenantResolveru jako každý jiný parametr
routy. Samotná tenancy zůstává, kde je — globální scope nad každým dotazem, ne
záležitost routování; viz Autorizace.
Zóny
Víc mount pointů nad jednou sadou resources — admin, business, production.
Resource může být v jedné z nich, ve víc, nebo ve všech: zóna násobí, kde je
stránka dosažitelná, ne kolikrát je zaregistrovaná.
Zóna je jméno route skupiny a nic víc:
Route::name('admin.')->prefix('admin')->middleware(['web','auth','can:admin']) ->group(fn () => Route::wireResources()); Route::name('business.')->prefix('business')->middleware(['web','auth','can:business']) ->group(fn () => Route::wireResources(only: ['orders']));
admin.wire.orders.index → admin/ordersbusiness.wire.orders.index → business/orders
Rozděluje je to volání name(). Vynech ho na druhé skupině a obě zóny
zaregistrují wire.orders.index, kde pozdější tiše vyhraje každý lookup — proto
je cesta přes config níž bezpečnější
způsob, jak zóny deklarovat: tam je zóna klíčem pole a zapomenout se nedá.
Které resources zóna obsahuje, říká only / except a nic jiného — žádný druhý
seznam, který by se musel držet v souladu s routami.
Odkazování uvnitř zóny. Každá otázka na URL zní „kde je tenhle klíč v téhle zóně“, takže zóna cestuje s ní:
ResourceRoutes::urlFor('orders', zone: 'business'); // /business/ordersResourceRoutes::urls(zone: 'business'); // jen to, co business routujeapp(Workspace::class)->navigation(zone: 'business'); // položky odkazující do business
Klíč, který zóna neroutuje, odpoví null a vykreslí se bez odkazu — přesně jako
neroutovaný resource. Když má menu obsahovat jen to, na co tahle zóna opravdu
dosáhne, řekni si o to:
app(Workspace::class)->navigation(zone: 'business', linkedOnly: true);
Volitelné, ne pravidlo, protože důvody, proč položka nemá URL, jsou dva různé
a Workspace je nerozliší: jedna může být routovaná v jiné zóně, druhá nikde.
A shell s vlastním URL schématem má tady bez odkazu úplně všechno a stejně chce
všechny položky — je to volající, kdo ví, ve kterém případě je. Skupina, které
vypadnou všechny položky, zmizí celá místo prázdného nadpisu.
Landing page zóny. /business samo neroutuje nic, dokud si to něco
nenárokuje — a nárokuje se to jednou metodou: prázdný prefix nepřidá segment,
takže index té stránky sedne na vlastní cestu skupiny:
final class BusinessOverview extends Dashboard implements ConfiguresRoutes, ProvidesPages{ public static function pages(): array { return ['index' => ShowBusinessOverview::class]; } public static function routePrefix(): ?string { return self::ROOT; } }
business.wire.business-overview.index → businessbusiness.wire.orders.index → business/orders
Která zóna přistane kde, říká only / except — jako každá jiná otázka na
členství: dejte každé zóně vlastní dashboard a vypište ho tam. Dvě stránky, které si
nárokují kořen jedné skupiny, jsou odmítnuty — Laravel klíčuje routy podle
URI, takže by druhá tu první nahradila i se jménem routy a zůstala by položka
menu, která vypadá zaroutovaně a tiše nikam neodkazuje.
Zóna, která chce cíl a ne vlastní stránku, napíše vedle skupiny obyčejný redirect:
Route::redirect('business', 'business/orders');
Odkud se zóna bere. Zone::current() ji přečte z routy, která se právě
vykresluje, a je to volání pro plný render stránky:
public ?string $zone = null; public function mount(): void{ $this->zone = Zone::current();}
Route::currentRouteName() během Livewire round tripu odpoví livewire.update,
takže komponenta, která se zeptá znovu uprostřed updatu, nedostane nic — a paleta,
která hledá při každém stisku klávesy, by odkazovala mimo svoji zónu a přitom
vypadala bezvadně. Přečtěte to jednou, uložte do public property a nechte to Livewire
přenášet. Command paleta to přesně tak dělá, takže paleta v zónovaném layoutu
nepotřebuje žádnou konfiguraci.
Registrace z configu místo route souboru
Macro výše zůstává referenční cestou. Aplikace, která chce konvenci a nechce si kvůli ní držet route soubor, předá tytéž argumenty skupiny jednou:
// config/wire-panels.php'routes' => [ 'enabled' => true, 'prefix' => 'admin', 'middleware' => ['web', 'auth'], 'domain' => null, 'only' => [], 'except' => [],],
Zóny jsou klíč zones a klíč pole je ta zóna:
'routes' => [ 'enabled' => true, 'middleware' => ['web', 'auth'], // dědí každá zóna 'zones' => [ 'admin' => [ 'prefix' => 'admin', 'middleware' => ['web', 'auth', 'can:admin'], ], 'business' => [ 'prefix' => 'business', 'only' => ['orders', 'customers'], ], ], ],
Každá zóna zdědí hodnoty mimo zones a přepíše to, co pojmenuje. Klíč se stane
prefixem jména routy, což je důvod dát tomuhle přednost před ručně psanými
skupinami, ne jen alternativa k nim: v route souboru je ->name('business.')
řádek, který se dá vynechat, a vynechání znamená, že jedna zóna tiše převezme
odkazy druhé. Klíč pole vynechat nejde a opakovat se nemůže.
Bez klíče zones je to jedna nepojmenovaná skupina, což je to, co chce
jednozónová aplikace.
Ve výchozím stavu vypnuté, a to záměrně: providery balíčků bootují dřív než vaše
vlastní, takže tyhle routy se matchují před vším v routes/web.php. Aplikace
s catch-all routou pod stejným prefixem dnes vyhraje a přestala by — to je
rozhodnutí, které se dělá, ne default, který se zdědí.
Zapnout tohle a zároveň volat Route::wireResources() by zaregistrovalo každou
stránku dvakrát pod jedním jménem routy; je to odmítnuto, ne smířeno, se zprávou,
která pojmenuje obě místa, kde stačí smazat řádek.
Jak na ně odkazovat
URL už nemusí nikdo psát ručně. Položka menu nese URL stránky svého klíče a výsledek hledání nese URL svého záznamu:
$item->getUrl(); // /admin/orders — doplní Workspace, null když neroutováno$result->url; // /admin/orders/7 — z klíče a klíče záznamu
Obojí přichází z ResolvesPageUrls, na které odpovídá wire-panels a na které
wire-core odpovídá null, když routing nevlastní žádný balíček. null je
plnohodnotná odpověď: položka menu bez href se vykreslí a resource, který
nedeklaruje stránky, je neodkazovaný záměrně. Položka nebo výsledek, který si URL
pojmenuje sám, vždy vyhraje — externí odkaz nebo aplikace s vlastním URL schématem
shellu.
Sáhnout po tom přímo je totéž volání:
ResourceRoutes::urlFor('orders'); // /admin/ordersResourceRoutes::urlFor('orders', 'edit', ['record' => 7]); // /admin/orders/7/editResourceRoutes::urls(); // ['orders' => '/admin/orders', …]
Full-page Livewire komponenta potřebuje layout a framework ho nedodává — nastavte
si livewire.component_layout na svůj vlastní.
Routing API
ResourceRoutes::all(array $only = [], array $except = []): array // každý klíč, který deklarujeResourceRoutes::for(string $class): array // jeden, nebo vyhodí výjimkuResourceRoutes::urlFor(string $key, string $page = 'index', array $parameters = [], ?string $zone = null): ?stringResourceRoutes::urls(string $page = 'index', ?string $zone = null): arrayResourceRoutes::uriFor(string $name, string|RoutePage $page): string // the segment it sits atResourceRoutes::takesRecord(string $name, string|RoutePage $page): bool // read off that URI, not off the kind
A dvě čtení jména routy stránky, která každý volající dostane z jednoho
ukotveného vzoru — livewire.update obsahuje wire., takže hledání podřetězce
hlásí zónu přesně na tom požadavku, který žádnou nemá:
Zone::current(): ?string // zóna právě vykreslované stránky — jen při plném renderuZone::of(?string $routeName): ?stringZone::currentPage(): ?string // druh té stránky — `index`, `view`, `edit`, nebo vlastní druh resourceZone::pageOf(?string $routeName): ?stringZone::prefix(?string $zone): string
urlFor() odpoví null ve dvou případech: když klíč nic neroutuje, a když routa
potřebuje parametr, který tohle volání nedalo — třeba resource na doméně
{tenant}. Obojí se vykreslí jako „bez odkazu“, místo aby to shodilo menu.
Z wire-core na to sáhni přes ResolvesPageUrls, na které odpovídá wire-panels
a které odpoví null, když routing nevlastní žádný balíček. RegistersPageRoutes
je druhá půlka toho seamu: wire-core ho zavolá ve chvíli, kdy jsou registry plné,
což je jediný okamžik, kdy routy z configu
můžou přečíst kompletní katalog.
Související
- Stránky — komponenty, na které tyhle routy vedou
- Navigace — odkud se bere URL položky menu
- Autorizace — gates, na které se ptá
permission()acan: - Konfigurace — celý blok
wire-panels.routes - Admin shell — layout pro stránky, na které tyhle routy vedou