První boj za hezké URL, aneb Router a jeho FILTER_IN, FILTER_OUT
- theacastus
- Člen | 81
Zdravím,
pustil jsem se do boje „za hezké URL“, tj aby adresa vypadala třeba
/user/admin
a ne /user/show/1
.
Jde mi vlastně o první krok, pak už budu schopný si vše implicitně
překopat, ale…
Na záznamu z přednášky Honzi Smitka (Posobota) https://www.youtube.com/watch?… jsem to celé prošel a
začal jsem zkoušet na vlastní pěst. V tuto chvíli takto vypadá část mé
RouterFactory.php
:
$frontRouter[] = new Route('user/<id>', array(
'presenter' => 'User',
'action' => 'show',
'id' => array(
Route::FILTER_IN => function ($id) use ($container) {
if (is_numeric($id)) {
return $id;
} else {
/** @var user \Nette\Database\Context */
$user = $container->NECO();
return $user->where('username', $id)->fetch()->id;
}
},
Route::FILTER_OUT => function ($id) use ($container) {
if (!is_numeric($id)) {
return $id;
} else {
/** @var user \Nette\Database\Context */
$user = $container->NECO();
return $user->get($id)->username;
}
}
)
));
Po prohlédnutí zjistíte že se tam nachází metoda NECO()
a
to kdyz chci získat přístup k databázi přes $container
.
PS: Ano, vím že psaní celé routy do RouterFactory není asi moc čisté,
ale jak bude zmíněno níže, s modely jsem si zatím moc
nepotykal…
EDIT: Landěnka dokonce hlásí že používám nedefinovanou proměnou
$container
, je čas naučit se s Modely
Ve videu je použita funkce createPages()
, ale nevím kde je
definovaná, co dělá apod. To jsem ve videu neviděl a zdroják jsem nenašel
:/. Ano, v ukázce jde o hezčí odkazy na stránky, ne o profily
uživatelů. Ale logika je stejná.
Samotný princip jsem pochopil, doufám, Route::FILTER_IN
překládá/transformuje hezkou URL do originální nette URL tj. dělá z
/user/admin
⇒ /user/show/1
a
Route::FILTER_OUT
dělá to samé druhým směrem. OUT zajištuje
to že v odkazech generovaných nette je použita nová hezká URL a IN že ji
umí zpracovat.
Takto jsem to pochopil ±, doufám že nejsem zas tak daleko od pravdy.
Následně jsem začal zápasit i s URL u dalších věcí jako jsou příspěvky na blogu a stránky typu /contact apod… (z originálního /intro/contact) a jako zdroj pro URI je sloupec v databázi, jenže přemýšlím o tom zda by nebylo vhodnější mít speciální tabulku jen pro URI.
Pokud by někdo věděl, jak Router přemluvit a naučit mě trochu používat Modely, bylo by to skvělé. Našel jsem například https://pla.nette.org/…ru-ve-filtru ale tam se řeší něco jiného a není vysvětleno jak funguje jejich router
$router[] = new Route('[!<lang>]/[!<article [a-z-]+>]', [
'presenter' => 'Article',
'action' => 'default',
'article' => [
Route::FILTER_IN => ['ArticleModel::slugToId'],
Route::FILTER_OUT => ['ArticleModel::idToSlug']
],
'lang' => 'cs',
]);
Konkrétně si nevím rady s
Route::FILTER_IN => ['ArticleModel::slugToId']
a
Route::FILTER_OUT => ['ArticleModel::idToSlug']
. Pochopil jsem
že odkazují na \Model\ArticleModel a jeho metudy slugToId
a
idTuSlug
. Ale nevím jak na to. Uživatelé, přihlašování
registrace ještě nemám hotové, takže UserManager a jiné modely zatím
nejsou.
A krom toho bojuji na další frontě s componenty, jako jsou přihlašovací a registrovací formuláře, sandbox mi dává zabrat :D
Editoval theacastus (11. 9. 2016 9:19)
- Oli
- Člen | 1215
Budeš potřebovat tohle: https://doc.nette.org/…dependencies#….
Pokud jsi to nečetl, tak si to přečti celé. Další věc je, že je dobré
mít v routě název presenteru. <presenter>
. Můžeš si ho
i přejmenovat.
Díky constructor injection si můžeš předat do konstruktoru
\Nette\Database\Context
, takže pak použiješ
// místo toho
/** @var user \Nette\Database\Context */
$user = $container->NECO();
// tohle
/** @var $user \Nette\Database\Selectize */
$user = $this->context->table('articles');
- theacastus
- Člen | 81
Díky, vrhám se do čtení. Co se týče presenteru, tak toto je jen pro
jeden konkrétní případ routy, protože upravuji
/user/show/<id>
na /user/<id>
, kde
<id> je username z databáze.
U dalších odkazů jako je například post na blogu to asi využiju, díky
cca o hodinu později
Tak jsem asi něco pochytil
$frontRouter[] = new Route('user/<id>', array(
'presenter' => 'User',
'action' => 'show',
'id' => array(
Route::FILTER_IN => function ($id) {
if (is_numeric($id)) {
return $id;
} else {
/** @var $user \Nette\Database\Selectize */
$user = $this->context->table('account');
return $user->where('username', $id)->fetch()->id;
}
},
Route::FILTER_OUT => function ($id) {
if (!is_numeric($id)) {
return $id;
} else {
/** @var $user \Nette\Database\Selectize */
$user = $this->context->table('account');
return $user->get($id)->username;
}
}
)
));
Je to upravené a laděnka hází chybu že se chovám k $this
jako k objektu i když to objekt není. Což je trochu zvláštní když jsem
v RouterFactory.php
kde je třída RouterFactory
. Ale
stejně si myslím že to nevykouzlí přístup k DB.
Editoval theacastus (11. 9. 2016 11:17)
- theacastus
- Člen | 81
@GEpic, když chci „vyhezkanou url“. Nebo si na to spíš mám sestavit model? – Návrhy jak na něj? Navíc by nebyl ve výsledku jediný a mít Model na každý hezký odkaz je hloupost, takže možná jeden velký Model nebo rovnou vlastní router ?
Jinak router v DB jsem viděl zde https://www.youtube.com/watch?…
- Petr Parolek
- Člen | 455
Ahoj, já bych dal jednodušše:
$router[] = new Route('user/<username>', array(
'presenter' => 'User',
'action' => 'show'
));
Spojení s databází přes model řeší presenter
Editoval ppar (11. 9. 2016 12:52)
- theacastus
- Člen | 81
@ppar to je hezké a to jsem měl i funkční, já jsem to asi nezobeznil jak jsem měl.
Ano, chtěl jsem si to na useru vyzkoušet – jako to bylo v přednášce.
Ale co třeba transformace postů na blogu, tj nechci
/blog/post/63
ale třeba
/p/63-dneska-jsem-se-ucil-s-modely
nebo alespoň
/blog/post/63-dneska-jsem-se-ucil-s-modely
. (druhý příklad
celkem brutálně porušuje „pravidlo“, že čím kratší url tím lépe)
Jinak číslo přidávám abych zaručil že url je unikátní a nebude to
později kolidovat s něčím
- Neumím s modely, což je celkem problém a snažím se to napravit, ale čtení teorie je jedna věc a praktická část mého učení je věc jiná.
- Chci zprovoznit uživatelské rozhraní a s tím spojené komponenty pro formuláře na přihlášení, registraci a vytvořit UserManager model…
PS: Nepohádejte se tady…
Editoval theacastus (11. 9. 2016 13:23)
- Landsman
- Člen | 152
@theacastus
S tím pravidlem krátkých adres bych si tolik hlavu nelámal. Unikátnost můžeš ověřovat před uložením do databáze (nevím jak generuješ slugy) a ID tam být nemusí, když nepovolíš uložení duplicity. Zpravidla se ti to ID ale bude hodit pro další věci :)
$router[] = new Route("p/<id>-<slug>", [
"presenter" => "Article",
"action" => "detail",
"id" => null,
"slug" => null
]);
Ty modely jsou první věc.
- Oli
- Člen | 1215
ppar napsal(a):
@Oli, nemíchej tu špatné návyky.
Jde o to, že se k tomu zatím nedostal. Nemyslím si, že je možné, pokud někdo začíná aby měl vše od začátku „tip ťop“. Jestliže se zatím necítí na model, na to že ho potřebuje, tak ho nech. Ať si zatím ošahá ostatní části. Pokud navíc sám přijde na to, proč model potřebuje (až ho to bude tlačit) tím líp (viz. Nešahej na ty kamna spálíš se|Au, sakra to bolí, už na ty kamna šahat nebudu ;-)).
Přesně, jak říká @ppar. To tvé monstrum je ohromný overkill a web by se ti zpomalil.
Používám to ve všech projektech. Není s tím problém. Pokud je problém, stačí to cachovat…
Jednoduše si přidej do svého modelu metodu na najítí uživatele podle username a v presenteru s tím pak pracuj.
To on ale nechce. On chce jinou URL. V aplikaci pak
můžeš nechat User:show 12
, ale v url budeš mít místo
user/show/12
třeba uzivatel/pepik-novaku
.
Takovej zlatej grál tohohle problému je http://zlml.cz/…ni-url-adres od @mrtnzlml
- GEpic
- Člen | 566
Používám to ve všech projektech. Není s tím problém. Pokud je problém, stačí to cachovat…
Já ti rozumím, ale pokud se nedostal k modelům, asi nemá smysl cachování vůbec zmiňovat.
Chápu že hezké URL jsou cool, ale na druhou stranu dají se řešit VŽDY v jakékoliv části vývoje. Navíc špatně nastavené routy BĚHEM vývoje dokáží kolikrát dost zatopit (navíc pokud začneme rozlišovat např. CLI).
Radši mám při vývoji defaultní routy, abych doopravdy viděl, co se tam vlastně děje.
Editoval GEpic (11. 9. 2016 14:54)
- Oli
- Člen | 1215
@GEpic s tím souhlasím. Podle mě je důležité si uvědomit, že se začíná učit pracovat s Nette. Možná i úplně programovat. Už je to dávno, co jsem se já učil programovat, ale vím, že největší pokrok jsem ze začátku viděl v tom co je vidět. Když použije model, tak to není navenek nikde vidět. URL je. To si myslím, že je ta motivace začít s touhle částí.
Cachování jsem zmínil „pro budoucnost“. Na localhostu ho to s 10 záznamy v db nebude omezovat. Ono ani na produkci s 500 záznamy by ho to neomezovalo.
Možná to není nejlepší místo, kde začít. Na druhou stranu je to vidět a někde se začít musí. :)
Myslím, že rejpat se v kodu a rikat, ted pouzij tohle a ted tohle a pak tohle a na venek neni nic vidět by začátečníka spíš odradilo (tak ja sedim 2. den u pc, rejpu se v tom a neni nikde nic videt)…
- GEpic
- Člen | 566
Já získal nejvíc moudrosti skrze funkci bdump()
.
A i objevení funkce bdump() bylo úžasné, protože do té doby jsem
používal Debugger::barDump()
.
Vím že trošku offtopic, za to se omlouvám. Ale nejvíc se toho člověk naučí zkoušením a sebezlepšováním, můžeme tu vést nekonečné debaty kde začít a co se učit, ale vše to stojí a padá čistě na tom jednom člověku.
- theacastus
- Člen | 81
GEpic napsal(a):
Vím že trošku offtopic, za to se omlouvám. Ale nejvíc se toho člověk naučí zkoušením a sebezlepšováním, můžeme tu vést nekonečné debaty kde začít a co se učit, ale vše to stojí a padá čistě na tom jednom člověku.
To se sebezlepšováním vás tu zmínilo hodně a jak řekl @Oli, v URL je ta změna vidět. V tuto chvíli se učím Nette, né že bych byl PHP profík. Ale když jsem poprvé nakoukl do Nette, templatů a práci s databází (tak půl roku zpět) a programovat jsem tak nějak začal 3 roky zpátky, ale Nette mi v mnoha věcech otevírá cestu. Teď jsem třeba měl a stále mám problém s Modely – jak postavit Model, k čemu a KDY je dobrý a do toho se mi tu míchají komponenty, továrničky a další „kouzla“. Samozřejmě že chci aby vše jelo hned ale něčím se začít musí, cachování to ale opravdu nebude
- Oli
- Člen | 1215
Trochu jsme se odklonili od tematu. :) Nevím, jestli jsi to už vyřešil, ale databázi do routeru dostaneš takhle: https://forum.nette.org/…zi-v-routeru#…
- theacastus
- Člen | 81
Dobrá, co jsem tak koumal a hrabal se ve svém kódu a po nějakém to přemýšlení je tahání databáze do routeru opravdu zlo, je tu někde na fóru, planette popř. jinde nějaký návod/dokumentace k Modelům a jeho prací s URL, URI a slugy ?
Popř. jestli někde máte nějaké basic model. Já se v tom stejně budu hrabat a budu to rozbíjet a následně spravovat, aneb já se učím v nette :D
- CZechBoY
- Člen | 3608
@theacastus mrkni třeba k čemu je DI a jak se to používá v Nette https://doc.nette.org/…introduction
- Oli
- Člen | 1215
Už jsem ti psal
Oli napsal(a):
Takovej zlatej grál tohohle problému je http://zlml.cz/…ni-url-adres od @mrtnzlml
Máš obecně 2 možnosti
- použít databázi (ideálně v nějaké repository|service třídě), kde si namapuješ ID-slu; ID-folder; ID-whatever
- všude v aplikaci předělat id na slug. Pokud se pak rozhodneš, že
chceš místo slugu třeba
id-slug
, tak to zase všude předělat.
Právě aby jsi zanesl závislost na databázi na jedno místo slouží ty
filtry. Pokud je nepoužiješ, tak máš závislost na třeba slugu napříč
aplikací. Pokud je použiješ, tak máš závislost na id
. A jen
v routeru pracuješ s tím slugem. Pokud to pak (z jakéhokoli důvodu)
přejmenuješ ze slug
na třeba url
, tak to upravíš
na asi 4 řádcích v routeru…
Proto je podle mě lepší řešení číslo 1 než 2.