Předávání persistentních parametrů ve formuláři
- m.brecher
- Generous Backer | 863
Zkoušel jsem předávání persistentních parametrů v presenteru a komponentě, tam funguje OK
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/** @persistent */
public string $test = '';
.....
}
Ale uvnitř formuláře se mě persistentní parametry rozchodit nepodařilo – zkoušel jsem to ve vlastní třídě poděděné z Nette\Application\UI\Form
class BaseForm extends Nette\Application\UI\Form
{
/** @persistent */
public string $test = '';
.....
}
Skoro to vypadá, že ve formulářích persistentní parametry Nette nepodporuje. Neporadil by někdo, zda to přeci jenom jde a zda nedělám někde nějakou chybu? Díky ;)
- Kamil Valenta
- Člen | 815
Persistentní parametr nepaří do Form ad-hoc, ale to Control
FormFactory…
Takže komponenta zpracuje persistentní parametr a vrátí Form, který už ho
využije…
- m.brecher
- Generous Backer | 863
Kamil Valenta napsal(a):
Persistentní parametr nepaří do Form ad-hoc, ale to Control FormFactory…
Takže komponenta zpracuje persistentní parametr a vrátí Form, který už ho využije…
@Kamil Valenta
Můžeš prosím Tě poslat nějaký příklad PHP kódu k použití persistentního paametru ve formuláři díky té Control FormFactory? Neumím si přdstavit jak na to, to by ta FormFactory měla dědit z třídy Nette Control?
- Kamil Valenta
- Člen | 815
Je to velmi podobné jako tady:
https://forum.nette.org/…any-datagrid?p=38
Form nebo Grid je v podstatě kompoenta, kterou obalíš do své komponenty, ve které si přenášíš persistentní parametry.
Takže máš:
class MyControl extends BaseControl {
/** @var $id @persistent */
public $id = null;
protected function createComponentTestForm() {
// tady si zavoláš create() z FormFactory...
}
}
- Kamil Valenta
- Člen | 815
Polki napsal(a):
A prosím neextenduj form. Není to potřeba, pokud si nepřidáváš například nějaké traity. Na 99,999% si vystačíš s FormFactory třídou
To se mi zdá jako odvážné tvrzení, odhadovat kdo si s čím vystačí. Těžko bych vzpomínal, kdy jsem si naopak vystačil. Mám hromadu rozšíření, ano, asi by je šlo napsat i jinak než extends, ale naopak se ptám, proč by to extendovat neměl. Chápu, že se dědičnost nemá nadužívat, ale zase bát se jí jako čert kříže mně připadá zbytečné…
- Polki
- Člen | 553
Kamil Valenta napsal(a):
Polki napsal(a):
A prosím neextenduj form. Není to potřeba, pokud si nepřidáváš například nějaké traity. Na 99,999% si vystačíš s FormFactory třídou
To se mi zdá jako odvážné tvrzení, odhadovat kdo si s čím vystačí. Těžko bych vzpomínal, kdy jsem si naopak vystačil. Mám hromadu rozšíření, ano, asi by je šlo napsat i jinak než extends, ale naopak se ptám, proč by to extendovat neměl. Chápu, že se dědičnost nemá nadužívat, ale zase bát se jí jako čert kříže mně připadá zbytečné…
Nejde o to bát se dědičnosti. Dědičnost je super, když se umí
použít. Dokonce je i rychlejší tvorba instance 1 třídy, která je
nějakým 5 potomkem, než tvorba 5 instancí, které jsou spolu
propojeny.
Jen člověk musí vědět, jak tu dědičnost použít.
A zrovna Nette formuláře jsou postaveny tak, aby se dědičnost mohla
využívat co nejméně. Pokud potřebuju přidat nějakou metodu
rozšiřující původní Nette\Application\UI\Form
například
chci aby uměly mé formuláře ->addZip()
, tak pak vytvořím
traitu, extendnu formulář, dám tam traitu, a používám můj
extended form…
Ale to je tak všechno. Už jsem viděl frajery, kteří se snažili měnit výchozí vykreslování formulářů, předávat nějaké speciální parametry, vykonávat vnitřně obsluhu formuláře v integrovaných metodách onSuccess, předdefinovat jim v konstruktoru výchozí inputy a validační pravidla atd.
No a včera jak jsme viděl toto vlákno, tak k tomu přibylo, aby
formuláře uměly přijmat perzistentní parametry. Kam se jako poděly
principy SOLID?
Formulář by se měl starat jen o to, aby byl formulářem. Ne o to, jak se
bude na jako stránce vykreslovat (k tomu je view), ne o to, na které
stránce s jakými parametry se zobrazí (od toho jsou controlery), ne o to,
jak se zpracuje (od toho jsou modely), ne od toho jaká pole by měl obsahovat a
jaké parametry přijmat (od toho josu továrny) a jak je validovat (zase
model), případně jak se překreslovat (zase view). A když tohle všechno
vyhodíme, tak nám zůstane jen klasická třída Form, případně naše
rozšířená třída o naše metody na přidání nových typů inputů,
která jak jsem psal v 99.999% nepotřebuje upravit díky dobrému návrhu
kódu. Pokud potřebujeme přepsat pro nějaký konkrétní případ náš
formulář na více místech, tak od toho slouží továrna. Vytvořit si
nějakou FormFactory, která bude obsahovat například modifikace jako
$form->getElementPrototype()->setAttribute('novalidate', 'novalidate');
Je lepší řešení, než extendovat form jak pominutý a toto nastavovat
natvrdo v konstruktoru a to hlavně z toho důvodu, že máš 1 továrnu,
která tvoří form a další továrny už jen rozšiřují tuto základní
továrnu a ideálně kompozicí. Tím se dostaneš k tomu, že máš kód lépe
navržen a dobře ho testuješ, protože konstrukci new Form()
máš jen na jednom místě a ostatní továrny volají tuto basic továrnu. No
a když potřebuješ globálně pro všechny formy přidat nějakou fičuru, tak
ji přidáš do té základní továrny. Z hlediska testování pak máš taky
výhodu, protože se ta basic FormFactory jednouše mockne a vracet bude jiný
nějaký zase mockovaný form a nemusíš vymýšlet kouzla aby si svůj divně
extendovaný form byl schopný nějak šikovně testovat apod..
Osobně jsem dělal na něco přes 500 projektů a extends u formů jsem použil 1× právě na definici vlastních inputů jako jsou addMobile, addZip, addPin apod..
- m.brecher
- Generous Backer | 863
@KamilValenta
Před dvěma lety jsem nakonec persistenci parametrů ve formuláři v POST vzdal. Nyní na druhý pokus jsem učinil tento závěr:
Ve formulářích NELZE žádným způsobem udělat parametr s rozsahem persistence jenom na POST formuláře. Prostě to nejde.
a) formulář v presenteru v POST přenese jenom presenter + action + některé (automaticky ne všechny) parametry a sice ty které jsou buďto persistentní v presenteru, nebo jsou uvedeny v metodě příslušné akce. Jinak ne. Jenže persistentní parametry mají rozsah na všechny akce presenteru, což se tady nehodí. Teoreticky je zde ještě jedna možnost nějak využít toho, že parametr uvedený v metodě akce se do POST formuláře přenese. To jsem nezkoušel, jenže já stejně chci formuláře mít v Control.
b) formulář v komponentě poděděné z Control – tam je situace horší než v presenteru. Persistentní parametr komponenty se přenáší do odkazů GET se signálem komponenty, ale do POST formuláře nikoliv. A protože v komponentě nemáme metody akcí, tak zde není už žádná jiná možnost.
Zkusil jsem input hidden a tudy cesta vede, komplikuje to sice data formuláře, není to čistý návrh, ale jsem vděčný, že to alespoň nějak jde.
Tak kdyby zase někdo někdy potřeboval přenášet parametry přes POST formuláře tak nějak takhle vypadá schůdná cesta:
class MyForm extends Control
{
public int $num = 1;
public function render(): void
{
.......
}
public function createComponentForm(): Form
{
$form = new Form;
$form->addSubmit('submit', 'Odeslat');
$form->addHidden('__num');
$form->onSuccess[] = $this->handleSuccess(...);
return $form;
}
public function handleSuccess(Form $form, ArrayHash $values): void
{
$this->num = (int)$values->__num; // bacha na nešikovné přetypování ?int v php !!
$this->num ++;
$form['__num']->setValue($this->num); // nepoužívat setDefaultValue() !!
}
}
Editoval m.brecher (26. 1. 2023 7:16)
- m.brecher
- Generous Backer | 863
Ahoj,
opravuji svoje tvrzení před rokem:
formulář v komponentě poděděné z Control – tam je situace horší než v presenteru. Persistentní parametr komponenty se přenáší do odkazů GET se signálem komponenty, ale do POST formuláře nikoliv.
Znovu jsem vyzkoušel persistentní parametr definovaný v komponentě použít ve vnořeném formuláři:
class TestForm extends Nette\Application\UI\Control
{
#[Persistent]
public int $num = 0;
public function createComponentForm()
{
$form = new Nette\Application\UI\Form;
for($i = 1; $i <= $this->num; $i++){
$form->addText('text_'.$i, 'Text '.$i);
}
$form->addSubmit('send', 'Odeslat');
$form->onSuccess[] = fn() => $this->num++;
return $form;
}
public function render()
{
$this['form']->render();
}
}
a funguje to – persistentní parametr komponenty se přenese při submitech formuláře, vyzkoušeno v nette/forms 3.1.11
- Higr
- Člen | 16
m.brecher napsal(a):
Ahoj,
opravuji svoje tvrzení před rokem:
formulář v komponentě poděděné z Control – tam je situace horší než v presenteru. Persistentní parametr komponenty se přenáší do odkazů GET se signálem komponenty, ale do POST formuláře nikoliv.
Znovu jsem vyzkoušel persistentní parametr definovaný v komponentě použít ve vnořeném formuláři:
class TestForm extends Nette\Application\UI\Control { #[Persistent] public int $num = 0; public function createComponentForm() { $form = new Nette\Application\UI\Form; for($i = 1; $i <= $this->num; $i++){ $form->addText('text_'.$i, 'Text '.$i); } $form->addSubmit('send', 'Odeslat'); $form->onSuccess[] = fn() => $this->num++; return $form; } public function render() { $this['form']->render(); } }
a funguje to – persistentní parametr komponenty se přenese při submitech formuláře, vyzkoušeno v nette/forms 3.1.11
Ahoj, zkoušel jsem to podle tvého příkladu, mám v presenteru handle kterým setnu $customId, to funguje správně ale jakmile form submitnu tak se $customId resetne zpět na 0, dělám něco špatně?
class FormFactory extends \Nette\Application\UI\Control
{
#[Persistent]
public int $customId = 0;
public function createCustomForm(callable $onSuccess): CustomForm
{
bdump($this->customId); // 0
$form = new CustomForm($this->customId); // extends Nette UI Form
$form->init();
$form->onSuccess[] = function() use ($onSuccess): void
{
$onSuccess();
};
return $form;
}
}
- m.brecher
- Generous Backer | 863
@Higr
Ahoj, zkoušel jsem to podle tvého příkladu, mám v presenteru handle kterým setnu $customId, to funguje správně ale jakmile form submitnu tak se $customId resetne zpět na 0, dělám něco špatně?
Pošli handle z presenteru + celou třídu CustomForm, takhle bez kódu je těžko říct, kde je chyba.
- Taco
- Člen | 50
Nette\Application\UI\Form
nepodporuje persistentní parametry,
protože neimplementuje rozhraní
Nette\Application\UI\StatePersistent
.
@Higr aby ti fungovali persistentní parametry i v komponentě, nestačí jen uvést, které parametry to mají být. Musíš to říct i presenteru.
- m.brecher
- Generous Backer | 863
@Taco
@Higr aby ti fungovali persistentní parametry i v komponentě, nestačí jen uvést, které parametry to mají být. Musíš to říct i presenteru.
Tak to není, podle dokumentace se persistentní parametry definují v třídě komponenty, zatímco atributem #[Persistent] u třídy presenteru se označí persistentní komponenty. Persistentní parametr a persistentní komponenta není totéž.