Eventy formuláře – matoucí předání parametrů

m.brecher
Generous Backer | 758
+
+1
-

Ahoj,

Zkoušel jsem různé varianty zpracování odeslaného formuláře a narazil na „matoucí“ způsob předání parametrů do callbacku eventu formuláře. Mám nainstalované PHP 8.1, nette/forms 3.1.7 a nette/application 3.1.7.

Odesílací tlačítko

V ovladači onClick[] si můžeme vyžádat typehintem předání odesílacího SubmitButton $submitter. Přitom event onClick[] se ani jinak navěsit nedá než na konkrétní tlačítko formuláře:

$form->addSubmit('createButton', 'Uložit')->onClick[] = $this->createButtonClick(...);

Pokud navěsíme na každé tlačítko jemu odpovídající ovladač, tak obvykle není v ovladači onClick[] potřeba $submitter řešit. Ale hodit se to může.

U ovladače onSubmit[] ale překvapivě předání SubmitButton $submitter k dispozici není, přitom právě zde by se tento parametr hodil více ;) Představme si např. formulář s třemi odesílacími tlačítky createButton v módu nový záznam, updateButton a deleteButton v módu editace záznamu. Můžeme celý formulář s tlačítky postavit ve Factory bez použití navěšování ->onClick[] a v presenteru provést jednoduchou distribuci obsluhy podle parametru $submitter:


// ukázka jak by se hodilo velmi mít v onSuccess[] parametr $submitter

public function createComponentMyForm(): Form
{
    $form = $this->myFormFactory->create();

    $form->onSuccess[] = fn(SubmitButton $submitter, ArrayHash $values) => match($submitter->name){
        'createButton' => $this->createFormSuccess($values),
        'updateButton' => $this->updateFormSuccess($values),
        'deleteButton' => $this->deleteFormSuccess($values),
        default => throw new AppException('Unhandled submitter name.'),
    };

    return $form;
}

Mix předání parametrů podle typu a podle pozice

Podle dokumentace:

https://doc.nette.org/…in-presenter#…
https://doc.nette.org/…in-presenter#…

i podle testování lze parametry do ovladačů onClick[], onSuccess[] předat takto:

první parametr Form|SubmitButton ⇒ předá buďto $form nebo $submitter (onSuccess[] $submitter ne)
druhý parametr ArrayHash|array|FormData ⇒ předá $values ve vyžádaném formátu

Pozor, parametr $values je na druhém místě, ale může být předán i na prvním místě.

V zásadě to není vůbec navrženo špatně – komfortně si vybereme typehintem co potřebujeme, jenom si musíme pamatovat, že je třeba dodržet pořadí parametrů + že není v onSuccess možné předat $submitter. Ale některé aspekty matou, jsou to tyto:

  • předává se podle typu Form, SubmitButton, ArrayHash, na první pozici může být cokoliv, připomíná to předávání služeb DI konstruktorem, kde na pořadí vůbec nezáleží
  • když máme jako první parametr Form funguje to i po přidání ArrayHash na druhou pozici, obráceně ne – musíme pořadí prohodit
  • v onSuccess[] nelze parametr SubmitButton předat vůbec, což je nelogické
  • datová třída pro data formuláře nemá předepsaný interface, který by se hodil, aby v tom měl člověk i Nette pořádek, takhle se může stát, že když omylem na druhé pozici člověk napíše Form $form, obdrží v proměnné prázdnou instanci Form, což se mě také v některých případech stalo.

Návrh:

  • předávat parametry Form, SubmitButton, ArrayHash|array|FormData VŠECHNY do obou ovladačů onClick[] i onSuccess[] nezávisle na pozici, ale výhradně podle typehintu
  • požadovat, aby datová třída formuláře povinně implementovala nějaký v Nette vestavěný interface (např. Nette\Application\UI\Forms\Formdata), podle kterého by Nette mohlo jednoznačně rozhodnout, zda je to třída, kterou je očekáváno předat, v kódu aplikace to udělá pořádek, není to práce navíc

Chápu, že situaci komplikuje zpětná kompatibilita – někdo možná nepoužívá typování. Zde by bylo vhodné ponechat pořadí parametrů. Při kombinaci jeden parametr otypovaný, druhý ne ponechat pořadí parametrů, nebo vyhodit výjimku a požadovat parametry buďto typované všechny nebo žádný.

Návrh je v souladu s moderními trendy v PHP a frameworcích směřujícími k pohodlnější syntaxi a využívání typů. Např. pojmenované parametry PHP 8.0 přinesly to pohodlí, že řešíme jenom název a nikoliv pozici parametru.

Editoval m.brecher (16. 10. 2022 1:35)