Sloučení rout pro slug i null
- Infanticide0
- Člen | 64
Ahoj,
mám routy:
$router->addRoute("all[/<page=1 \d+>][/<perPage \d+>]", "List:default");
$router->addRoute("<user>[/<page=1 \d+>][/<perPage \d+>]", ...);
Routa <user> předává entitu Usera do Presenteru a
ta je pak použita pro filtrování v DB a výpis dat se stránkováním.
Když je $user null, nic se nefiltruje a zobrazí se komplet data.
Routa /all je pro zobrazení stránky, kde je zobrazeno všechno.
- Jde tyhle routy nějak sloučit? Abych nemusel duplikovat parametry rout.
- <page> je tam pro Paginator, $page a $perPage bere z action/default parametrů. Neni lepší mít tyhle hodnoty jako Persistent? Co je tady best practice?
Díky za rady.
- uestla
- Backer | 796
Nešly by na to použít filtry a překlady? Něco ve stylu
$router->addRoute("<user>[/<page=1 \d+>][/<perPage \d+>]", [
'presenter' => 'List',
'action' => 'default',
'user' => [
Route::FilterTable => [
'all' => null,
],
],
]);
- m.brecher
- Generous Backer | 765
@Infanticide0
Když je $user null, nic se nefiltruje a zobrazí se komplet data.
Routa /all je pro zobrazení stránky, kde je zobrazeno všechno.
Takhle bez detailních informací mě tyto dvě stránky přijdou duplicitní – obě zobrazí všechno (komplet data). Otázkou je, pokud duplicitní nejsou čím se liší a pokud duplicitní jsou, proč routu /all jednoduše neodstranit, nebo ji ponechat a pro $user = null vyhodit 404.
- Infanticide0
- Člen | 64
uestla napsal(a):
Nešly by na to použít filtry a překlady? Něco ve stylu
$router->addRoute("<user>[/<page=1 \d+>][/<perPage \d+>]", [ 'presenter' => 'List', 'action' => 'default', 'user' => [ Route::FilterTable => [ 'all' => null, ], ], ]);
Dík za tip, zkusil jsem a asi by to fungovalo kdybych nepředával entity přímo do Presenteru přes FilterIn/Out, takhle to vyhodí chybu.
$router->addRoute("<user>[/<page=1 \d+>][/<perPage \d+>]", [
'presenter' => 'List',
'action' => 'default',
'user' => [
Route::FilterTable => [
'all' => null,
],
Route::FilterIn => function (string $user) {
return $this->orm->users->getById($user]);
},
Route::FilterOut => function (User|null $user) {
return $user->id;
},
],
]);
- Infanticide0
- Člen | 64
m.brecher napsal(a):
@Infanticide0
Když je $user null, nic se nefiltruje a zobrazí se komplet data.
Routa /all je pro zobrazení stránky, kde je zobrazeno všechno.Takhle bez detailních informací mě tyto dvě stránky přijdou duplicitní – obě zobrazí všechno (komplet data). Otázkou je, pokud duplicitní nejsou čím se liší a pokud duplicitní jsou, proč routu /all jednoduše neodstranit, nebo ji ponechat a pro $user = null vyhodit 404.
Obě routy míří na jeden Presenter, ale neni to Homepage, takže routu
potřebuje.
Presenter s $user=null zobrazuje třeba Datagrid všech dat ->findAll(),
když $user je entita, Datagrid podle jeho id filtruje data
->findBy(..$user->id..).
zjednodušeně
.cz/123456 ⇒ data uživatele ID=123456 (ListPresenter)
.cz/all ⇒ data všech uživatelů (ListPresenter)
.cz ⇒ HomePresenter
A já nechci duplikovat parametry. Hledám něco jako <user ?? ‚all‘>
- m.brecher
- Generous Backer | 765
@Infanticide0
takže jestli jsem to správně pochopil, tak chceš aby url vypadaly takto:
.cz/123456 ⇒ data uživatele ID=123456 (ListPresenter)
.cz/all ⇒ data všech uživatelů (ListPresenter)
.cz ⇒ HomePresenter
Jde to samozřejmě udělat, třeba nějak takhle:
$router->addRoute('', 'Homepage:default');
$router->addRoute('<user all|\d+>[/<page>][/<perPage>]', 'List:default');
A v presenteru:
public function actionDefault(string $user, ?int $page = 1, ?int $perPage = 20)
{
if($user === 'all'){
//.....
}
}
Proč jsem odstranil regulární výrazy pro $page a $perPage? Protože je
lepší typovat int v signatuře metody akce než v routě.
Naopak v parametru <user> se regulární výrazy musí použít, protože
jedině tak lze mít jednu routu pro all i pro single user. UserId ale
dostaneš jako string.
Editoval m.brecher (28. 2. 2:58)
- uestla
- Backer | 796
@Infanticide0 Aha, nedošlo mi, že už v té routě budeš mít
Route::FilterIn
a Route::FilterOut
– v tom
případě si tu „prázdnou“ hodnotu all
můžeš pořešit
už tam:
$router->addRoute("<user>[/<page=1 \d+>][/<perPage \d+>]", [
'presenter' => 'List',
'action' => 'default',
'user' => [
Route::FilterIn => function (string $user) {
return $user === 'all' ? null : $this->orm->users->getById($user);
},
Route::FilterOut => function (User|null $user) {
return $user === null ? 'all' : $user->id;
},
],
]);
- Kamil Valenta
- Člen | 762
m.brecher napsal(a):
Proč jsem odstranil regulární výrazy pro $page a $perPage? Protože je lepší typovat int v signatuře metody akce než v routě.
Proč je to lepší? S původní routou url „/all/ahoj“ skončí na 404
(nebo to matchne jiná routa, pro kterou to validní bude), s upravenou routou
to spadne na 500 (a žádná jiná routa nedostane příležitost, ačkoliv by
pro to třeba měla validní response).
V routě nejde o typování. Tazatel možná v metodě typováno má, routa
má být každopádně co nejstriktnější.
- m.brecher
- Generous Backer | 765
@KamilValenta
routa má být každopádně co nejstriktnější
Hodně záleží na celkové situaci, takže nejde generalizovat, ale s typováním int již v routě nemám dobré zkušenosti. Při neplatném parametru router vyhodí 404 a chybí informace, ve kterém modulu a presenetru se tak stalo. Obvykle chceme 404 stránky vykreslovat do layoutu aktuálního modulu a to je v tomto případě problém. Naopak když se bude int parametru typovat až v metodě akce tak 404 vyhodí presenter a error presenter bude vědět ve kterém modulu se to odehrálo a vykreslí 404 stránku do layoutu správného modulu. Typování int parametru v routě samo o sobě žádné výhody nemá.
- Infanticide0
- Člen | 64
Route::FilterIn => function (string $user) {
return $user === 'all' ? null : $this->orm->users->getById($user);
},
Route::FilterOut => function (User|null $user) {
return $user === null ? 'all' : $user->id;
},
Když FilterIn vrátí null, neznamená to, že router nenašel svůj „cíl“ a má jít hledat další routu?
- Kamil Valenta
- Člen | 762
m.brecher napsal(a):
Hodně záleží na celkové situaci, takže nejde generalizovat
Celkem nezáleží, nedává smysl, aby router propustil pro něj nevalidní
request a pak jej zařízl presenter.
Když pro zjednodušení přehlédnu filtrIn funkce, které do toho vnesou
ještě větší přísnost, uvažujme dvě routy, jedna matchne čísla, druhá
stringy:
$router->addRoute('<personalnumber>', ':Personal:List:default');
$router->addRoute('<slug>', ':Articles:Detail:default');
Pokud bude router moc benevolentní, url „/novinky-v-roce-2024“ se matchne na modul Personal a vyhodil by 404, sice krásně omalovanou layoutem z modulu Personal, ale druhá routa by ráda vrátila článek, který má…
ale s typováním int již v routě
Routy skutečně netypují.
Při neplatném parametru router vyhodí 404 a chybí informace, ve kterém modulu a presenetru se tak stalo. Obvykle chceme 404 stránky vykreslovat do layoutu aktuálního modulu a to je v tomto případě problém.
„Obvykle“ není „vždy“, ba dokonce možná ani ne
„většinou“.
URL „/dvacetsedm“ je dle výše uvedených rout adept na 404 jakého
modulu? Je to neexistující slug, nebo špatně zadané číslo?
Každopádně to není práce pro router. Ten má říct, zda je URL validní,
nebo 404.
ErrorPresenter může o layoutu rozhodnout z debug_backtrace() a nebo query
stringu.
Naopak když se bude int parametru typovat až v metodě akce tak 404 vyhodí presenter a error presenter bude vědět ve kterém modulu se to odehrálo.
Nikoliv. ErrorPresenter maximálně bude vědět, která routa byla „nejhladovější“ a matchnula to třeba i neprávem.
Editoval Kamil Valenta (28. 2. 19:40)
- m.brecher
- Generous Backer | 765
@Infanticide0
aby kód pro sloučené routy byl skutečně funkční, je potřeba zajistit, aby router mohl rozlišit nepovinné parametry $page a $perPage, které jsou za sebou, stačí dodat prefixy:
$router->addRoute('', 'Homepage:default');
$router->addRoute('<user all|\d+>[/page-<page>][/perPage-<perPage>]', 'List:default');
- Infanticide0
- Člen | 64
m.brecher napsal(a):
@Infanticide0
aby kód pro sloučené routy byl skutečně funkční, je potřeba zajistit, aby router mohl rozlišit nepovinné parametry $page a $perPage, které jsou za sebou, stačí dodat prefixy:
$router->addRoute('', 'Homepage:default'); $router->addRoute('<user all|\d+>[/page-<page>][/perPage-<perPage>]', 'List:default');
Prefixy tam používám, jen jsem je do ukázky nedával pro zjednodušení,
nejsou předmětem problému.
Vyházel jsem všechno mimo <user>, výraz .+ je špatně kvůli
lomítkům, ale to teď ignoruju. Typy v routeru jsem taky odstranil, kdyby se
na pozadí něco přetypovávalo.
S null
to prostě nefunguje. Proč?
{link UserList: $adminUser} => /1-admin
{UserList:default, user: users} => /users
{UserList:default, user: null} => #error: No route for Front:UserList:default()
public function actionDefault(User|null|string $user): void
{
if($user instanceof User)
$this->user = $user;
}
$router->addRoute("<user users|.+>", [
'presenter' => 'UserList',
'action' => 'default',
'user' => [
Route::FilterIn => function ($user) {
bdump($user, "in");
if($user === "users" || $user === null)
return false;
$up = explode('-', $user);
return $this->orm->users->getBy(["id" => $up[0]]);
},
Route::FilterOut => function ($user) {
bdump($user, "out");
if($user instanceof User)
return "$user->id-admin";
return "users";
},
],
]);
- m.brecher
- Generous Backer | 765
@KamilValenta
nedává smysl, aby router propustil pro něj nevalidní request a pak jej zařízl presenter.
Já to takto používám všude. V akci parametry requestu vždycky typuji. Potom je filtr regulárním výrazem \d+ v routě nadbytečný.
Pokud ovšem v jednom parametru chceme typem rozlišit dvě různé akce, pak je filtr v routě samozřejmě nutný. Také jsem to takto použil v parametru user:
$router->addRoute('<user all|\d+>.......', .....);
Parametry $page a $perPage žádnou paralelní routu nemají, filtry \d+ lze tedy vynechat a funkce zůstane stejná – proto jsem je tam nedal.
- m.brecher
- Generous Backer | 765
@Infanticide0
S null to prostě nefunguje. Proč?
Protože to routa neumožňuje. Tak jak jsou routy navrženy tak když je $user = null tak je to url homepage. Homepage ale má jiný presenter. Proto do odkazů nesmíš dávat $user = null a odkazovat přitom na UserList:default.
Pokud jsou routy takto:
$router->addRoute('', 'Homepage:default');
$router->addRoute('<user all|\d+>[/page-<page>][/perPage-<perPage>]', 'List:default');
Platné odkazy jsou tyto:
{link 'List:default', $user}
{link 'List:default', 'all'}
{link 'Homepage:default'}
Všimni si tohoto:
'<user all|\d+>'
Tento regulární výraz pro parametr $user povolí jenom ‚all‘ nebo integer, null nikoliv. A tak Jsi to přece úplně na začátku chtěl ne?
Editoval m.brecher (29. 2. 20:45)
- m.brecher
- Generous Backer | 765
@Infanticide0
S null to prostě nefunguje. Proč?
Až teď jsem si všiml, že používáš jinou routu:
$router->addRoute("<user users|.+>", [
'presenter' => 'UserList',
'action' => 'default',
'user' => [
Route::FilterIn => function ($user) {
bdump($user, "in");
if($user === "users" || $user === null)
return false;
$up = explode('-', $user);
return $this->orm->users->getBy(["id" => $up[0]]);
},
Route::FilterOut => function ($user) {
bdump($user, "out");
if($user instanceof User)
return "$user->id-admin";
return "users";
},
],
]);
Zde je pointa v primárním filtru parametru user v routě:
'<user users|.+>'
Použitý regulární výraz null nezachytí.
Tento regulární výraz by se dal zjednodušit:
'<user .+>' // jednodušší
'<user>' // ještě jednodušší
všechny varianty pro <user> matchnou jakýkoliv řetězec s výjimkou prázdného
ale routa by měla zachycovat jenom users nebo userId které je int, pak je lépe ji napsat takto:
$router->addRoute("<user users|\d+>", ...);
Routa kterou Ti navrhuji null nematchne, což je dobře, protože to by nedávalo smysl. Výpis pro všechny je pro $user === ‚users‘, co by se mělo vypisovat pro null ?? Tam není co vypsat.
Editoval m.brecher (29. 2. 21:14)