Falešný User Warning Invalid link
- m.brecher
- Generous Backer | 864
Ahoj,
předem se omlouvám za případný falešný poplach, ale s 90% jistotou si myslím, že jsem narazil na chybu v LinkGenerátoru.
Router.php:
$router->addRoute('[<lang en>/][<slug (?!admin).*>]', [
'presenter' => [ Route::VALUE => 'Front'],
'action' => [ Route::VALUE => 'article'],
'lang' => [ Route::VALUE => 'cs'],
'slug' => [ Route::VALUE => '@homepage'],
]);
$router->addRoute('admin[/<presenter>/<action>[/<id \d+>]]', 'AdminEntry:default');
FrontPresenter.php:
public function renderArticle(string $slug)
{
$article = $this->articleModel->getOne($slug);
.....
}
Česká homepage (url /) – Nette hlásí chybu:
User Warning
Invalid link: Missing parameter $slug required by App\Presenters\FrontPresenter::renderArticle()
Jedná se o tento link v menu.latte:
<a n:href="Front:article" class="button">Homepage</a>
Hodnota parametru $slug ve FrontPresenteru:
public function renderArticle(string $slug)
{
bdump($slug) // '$homepage' - to je správně
.....
}
Parametr $slug má správnou hodnotu, link je funkční – ověřeno kliknutím, ale Nette hlásí User Warning – FALEŠNÝ.
Zkusím obejít předání parametru $slug do akce – FrontPresenter.php:
public function renderArticle()
{
$slug = $this->getParameter('slug');
$article = $this->articleModel->getOne($slug);
.....
}
Funguje stejně jako v první variantě, ale User Warning NENÍ.
Bezpochyby je chyba v Nette – Třída vyhazující User Warning nebere v potaz defaultní hodnoty definované v routeru.
Editoval m.brecher (13. 1. 2023 2:57)
- m.brecher
- Generous Backer | 864
@DavidGrudl
Je to tak, hodnoty v routeru se neberou v potaz.
A je to vlastně dobře. „Falešný“ warning je totiž užitečné upozornění na chybějící parametr v odkazu do akce. Ono je totiž dobrou praxí parametry v odkazech explicitně uvádět, i když mají v routeru definovanou hodnotu.
Takže vlastně správný postup je uvést v odkazu hodnotu parametru i když ta hodnota je defaultní.
<a n:href="Front:article" class="button">Homepage</a> // není zřejmé, že odkaz má parametr
<a n:href="Front:article, slug: '@homepage'" class="button">Homepage</a> // tenhle kód je srozumitelný
Editoval m.brecher (13. 1. 2023 17:20)
- m.brecher
- Generous Backer | 864
@MajklNajt
Ale presenter nie je závislý na routeri, a teda volať tu metódu bez argumentu, ak je vyžadovaný, je IMHO chyba návrhu, nie chyba routera
Ono je to přesně naopak, když jsem volal metodu BEZ argumentu tak to fungovalo bez Warningu Nette:
public function renderArticle()
{
$slug = $this->getParameter('slug');
$article = $this->articleModel->getOne($slug);
.....
}
Naproti tomu, když jsem volal metodu S UVEDENÍM argumentu, tak to sice fungovalo také, ale Nette vyhodilo ten Warning:
public function renderArticle(string $slug)
{
$article = $this->articleModel->getOne($slug);
.....
}
Je to v Nette udělané tak (jak píše v tomto vlákně @DavidGrudl), že pokud je metoda akce volána s parametrem, potom Nette kontroluje, zda ve všech odkazech do té akce je ten parametr uvedený, pokud není vyhazuje Warning. Nette přitom NEKONTROLUJE, zda Router má nastavenu defaultní hodnotu parametru, pokud ji má, tak odkazy jsou funkční i když ten parametr chybí a přesto se vyhodí Warning.
Zpočátku mě přišlo, že ten Warning když odkazy fungují je vlastně chyba, ale teď si myslím, že je to správně, protože parametry by se v odkazech do akcí měly uvádět vždycky. Navíc by to bylo zbytečně složité, aby se vedle odkazů kontrolovaly i defaultní hodnoty v Routeru.
- MajklNajt
- Člen | 494
@m.brecher asi sa nechápeme :) Zhodneme sa na tom, že správanie routera a teda aj ten warning má opodstatnenie, a práve to sa snažím vysvetliť, že sa nemôžem spoliehať na niekoho ďalšieho, že mi niekde nejakú default hodnotu dosadí sám.
A teda že mám metódu:
<?php
public function renderArticle(string $slug)
{
if($slug === "@homepage") { /* do anything special */ }
$article = $this->articleModel->getOne($slug);
...
}
?>
tak link musím vytvárať vždy s uvedením slugu a skutočnosť, že
metóda akceptuje hodnotu slugu @homepage
ako nejakú default
hodnotu a podľa nej sa správa ináč ako obvykle, to nie je súčasťou jej
verejného rozhrania. V tomto prípade teda link musím tvoriť
vždy <a n:href="Front:article, slug: '@homepage'" class="button">Homepage</a>
Avšak ak chcem už v definícii metódy povedať, že má nejakú default hodnotu (bez toho aby som niečo písal do routra), mala by byť zapísaná takto:
<?php
public function renderArticle(string $slug = "@homepage")
{
if($slug === "@homepage") { /* do anything special */ }
$article = $this->articleModel->getOne($slug);
...
}
?>
V tomto prípade môžem link tvoriť tak, ako si to skúšal na začiatku
príspevku bez uvedenia slugu
<a n:href="Front:article" class="button">Homepage</a>
.
A toto mi z hľadiska čitateľnosti, zrozumiteľnosti a predvídateľsnosti správania príde vhodnejšie, toť vsio :)
- MajklNajt
- Člen | 494
A ešte pre úplnosť, osobne preferujem používať ako default
null
, príde mi to IMHO čistejšie, lebo to hovorí, že
$slug
môže byť null
(inými slovami
undefined
), ale to už je len taká slovná hračka:
<?php
public function renderArticle(?string $slug = null)
{
if($slug === null) { /* do anything special */ }
$article = $this->articleModel->getOne($slug);
...
}
?>
- m.brecher
- Generous Backer | 864
@MajklNajt
osobne preferujem používať ako default null
Ano, obecně souhlasím – null jako default hodnotu jakéhokoliv parametru je ideální. Url je ale specifická záležitost, chceme mít cool url – mimo jiné aby homepage neměla žádný slug. Hodnota null se pro slug homepage ale nehodí, třeba když bych chtěl v databázi mít ve sloupci slug unikátní index, tak hodnota null se do unikátního indexu nepočítá. Proto v případě slug je lepší null výjimečně nepoužívat.
- m.brecher
- Generous Backer | 864
@MajklNajt
asi sa nechápeme :)
Já to celkem chápu, co myslíš, víceméně proti tomu nic nenamítám.
Defaultní hodnotu mohu parametru nastavit i v metodě akce – jak píšeš. Potom ale musím být opatrný, protože ten parametr pak má v různých místech presenteru různou hodnotu:
Příklad z presenteru – parametr není v url:
public renderArticle(?string $param = 'homepage')
{
dump($param); // 'homepage'
// ale:
dump($this->getParameter('param')); // null
}
Naproti tomu, když definuji defaultní hodnotu v Routeru, tak to nejde obejít.