Životní cyklus presenteru a POST-Redirect-GET formuláře

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
setka
Člen | 10
+
0
-

Zdravím, narazil jsem na jeden problém, který je nejvíc patrný při zpracování formulářů s využitím doporučovaného patternu PRG (HTTP přesměrování po validním odeslání formuláře).

Situaci bych ilustroval na jednoduchém příkladu: stránka (třeba blogu) s výpisem článku, pod článkem formulář pro přidání komentáře a výpis diskuse.

Formulář (AppForm) se vytváří v presenteru pomocí továrničky. A co je důležité: data pro šablonu (obsah článku, komentáře) se načítají z databáze v actionVypisClanku metodě presenteru.

Co se stane v okamžiku odeslání fomuláře? Zavolá se příslušný signál (URL s parametrem ?do=něco), který formulář zvaliduje a je-li validní, volá definovanou onSubmit metodu. Validní odeslání formuláře nakonec přidá komentář do databáze a v duchu PRG provede HTTP redirect (obvykle na 'this', ale klidně jinam).

Zrada ale je, že ještě před zpracováním formuláře (resp. jeho signálu) se volala metoda actionVypisClanku, která se úplně zbytečně připojila k databázi a načetla z ní třeba text článku, který jsme nevyužili!

Zatím používám řešení, kdy obsah pro šablonu z databáze načítám až v metodě renderVypisClanku, která se volá až po zpracování formuláře. Potom ale pro Action metody nemám využití, a nemůžu ani používat užitečné „přepínání View“ podle https://doc.nette.org/…tion-vs-view

Navíc: pokud se podívám na obrázek cyklu presenteru na stránce https://doc.nette.org/…tion-vs-view , tak posledním okamžikem pro přesměrování je Action. V případě formulářů, jak jsem psal výše, ale přesměrovávám až v handle<Signal>!

Vzchází mi z toho, že by měla existovat ještě jedna fáze před Action, která by se používala právě na zpracování formulářů. Nebo jsem něco přehlédl a dívám se na celou věc špatně?

Ondřej Mirtes
Člen | 1536
+
0
-

Nepochopil jsem poslední dva odstavce, asi jsi myslel obrázek tady. Nevím, proč by před action měla být ještě jedna fáze, když zpracování formulářů probíhá právě až po ní.

Cos popsal, tak děláš dobře, jsem příjemně překvapen, že nováček na fóru zná PRG pattern a frameworku očividně rozumí :)

K tvému dotazu – k plnění šablony musí docházet až ve fázi render, k čemuž je také určena. Proč je životní cyklus presenteru postaven takhle? Protože v handle fázi (kam patří i zpracování formulářů) může dojít k nějaké akci, která ovlivní aktuálně vykreslovanou stránku – např. smažeš jednu položku z článků (a vykresluješ seznam článků). Kdybys šablonu naplnil už v action a v handle jeden článek smazal, tak ho po vykreslení stále uvidíš, ale přitom už nebude existovat.

Action fáze slouží např. k rozhodovací logice, zda-li se má Presenter na pozadí (aniž by o tom návštěvník věděl) na jiné view, nebo např. pro naplnění formuláře defaultními hodnotami, nebo pro nějaké jeho dodatečné nastavení (např. pokud v něm potřebuješ v nějakém hidden poli určit, jakou položku právě edituješ). V hodně případech tvoje presentery vůbec action metody mít nemusí.

Když se na to takhle podíváš, tak v action metodě opravdu nic zbytečného (co se při vykreslení formuláře nepoužije) být nemusí. Protože i když tam na základě nějaké logiky (třeba i po kontrole položky v databázi) přepneš view a zároveň formulář splňuje požadavky pro PRG pattern, k vykreslení stránky dojít může – např. když má uživatel vypnutý Javascript a nevalidně vyplnil formulář, anebo pokud na základě nějakého rozhodování v modelu na formulář zavoláš vlastní $form->addError("zpráva").

Ale i kdyby u tebe nastala situace, že něco provádíš v action metodě a přišels na to, že to pro zpracování formuláře není nezbytné, nelámal bych si s tím hlavu. On to server určitě vydrží a ty se můžeš zabývat důležitějšími věcmi :)

setka
Člen | 10
+
0
-

Ondřej Mirtes napsal(a):

Nepochopil jsem poslední dva odstavce, asi jsi myslel obrázek tady. Nevím, proč by před action měla být ještě jedna fáze, když zpracování formulářů probíhá právě až po ní.

Omlouvám se, vložil jsem špatný odkaz, ale přesně tenhle obrázek jsem myslel. Po delším uvažovaní souvislostí jsem právě došel k tomu, že Action skoro nikdy nevyužiju, a nějak jsem cítil, že to možná nedělám úplně správně.

Na tom obrázku je naznačeno (obdélníčkem Redirect?) a přesněji popsáno v textu, že Action je poslední okamžik pro přesměrování. Pochopil jsem z toho, že v handleXXX už by se přesměrovávat nemělo. No ale už jsem tady na fóru viděl obecně a rozumné doporučení, že po každém zpracování signálu, které mění stav (třeba smazání nějaké položky) by se mělo přesměrovat.

Je tedy „v duchu Frameworku“ přesměrovávat ve fázi zpracování signálů (handle)? Pokud ano, pak mě ten obrázek trochu zmátl, ale všechno je v pořádku.

Cos popsal, tak děláš dobře, jsem příjemně překvapen, že nováček na fóru zná PRG pattern a frameworku očividně rozumí :)

Nováček na fóru nemusí být nutně nováček v PHP/programování :) Při hledání frameworku jsem se nějakou dobu zabýval Zendem (několik lidí mi ho doporučilo, dokumentace se zdála dobrá), ale koukal jsem i na situaci okolo Nette. Když jsem v Zendu řešil formuláře a šablony, a přitom viděl příklady jak to řeší Nette, hned jsem poznal, že Nette na to jde „chytře“ a „tak jak bych to udělal sám (ale za dlouho a několikrát bych to mezitím předělal)“.
Z pohledu nového uživatele Nette se mi zdá současná dokumentace dostačující. Jak fungují složitější věci jde krásně dohledat ze zdrojáku, naopak kód Zendu je hnus a jeho dokumentace pokrývá jen základy.

K tvému dotazu – k plnění šablony musí docházet až ve fázi render, k čemuž je také určena. Proč je životní cyklus presenteru postaven takhle? Protože v handle fázi (kam patří i zpracování formulářů) může dojít k nějaké akci, která ovlivní aktuálně vykreslovanou stránku – např. smažeš jednu položku z článků (a vykresluješ seznam článků). Kdybys šablonu naplnil už v action a v handle jeden článek smazal, tak ho po vykreslení stále uvidíš, ale přitom už nebude existovat.

Přesně takhle jsem uvažoval, ale trochu mě zmátl třeba tenle post: https://forum.nette.org/…cni-formular?… (pravda, nejde přímo o plnění šablony, ale o výchozí hodnoty ve formuláři, což vnímám jako to samé). Podle mě je nastavování Defaults takhle zbytečné, protože při validním odeslání formuláře výchozí hodnoty nepotřebuju, ale zbytečně je načítám. Takže je lepší na tohle použít radší render<View>.

Action fáze slouží např. k rozhodovací logice, zda-li se má Presenter na pozadí (aniž by o tom návštěvník věděl) na jiné view, nebo např. pro naplnění formuláře defaultními hodnotami, nebo pro nějaké jeho dodatečné nastavení (např. pokud v něm potřebuješ v nějakém hidden poli určit, jakou položku právě edituješ). V hodně případech tvoje presentery vůbec action metody mít nemusí.

No právě já i to plnění formulářů výchozími hodnotami dělám v Render (protože až tam otevírám databázi). Takže Action využiju tím spíš méně často.

Když se na to takhle podíváš, tak v action metodě opravdu nic zbytečného (co se při vykreslení formuláře nepoužije) být nemusí. Protože i když tam na základě nějaké logiky (třeba i po kontrole položky v databázi) přepneš view a zároveň formulář splňuje požadavky pro PRG pattern, k vykreslení stránky dojít může – např. když má uživatel vypnutý Javascript a nevalidně vyplnil formulář, anebo pokud na základě nějakého rozhodování v modelu na formulář zavoláš vlastní $form->addError("zpráva").

Ale i kdyby u tebe nastala situace, že něco provádíš v action metodě a přišels na to, že to pro zpracování formuláře není nezbytné, nelámal bych si s tím hlavu. On to server určitě vydrží a ty se můžeš zabývat důležitějšími věcmi :)

Jasné.

Ještě jedna otázka: Má nějaký význam, že se signály zpracovávají až po Action? Z mého pohledu by to bylo lepší obráceně. Neumím si teď představit signál, jehož zpracování by záviselo na provedení Action. Leda že by v Action proběhlo přesměrování, ale to by se zas takový signál „ztratil“, takže mi taková situace nedává smysl.

Ale věřím, že to je teď vymyšlené dobře.

Ondřej Mirtes
Člen | 1536
+
0
-

To s tím „přesměrováním nejpozději v action“ se týká té metody $this->setView(), na volání $this->redirect() není nic špatného ani v renderu. V handle se toho využívá při PRG vždycky, to jinde ani nejde :) Tohle je v dokumentaci popsané docela nešťastně.

Plnění defaultních hodnot v render – jo, lidé to tak dělají a nenapadá mě zrovna nějaký problém, který by s tím mohl nastat. Ale přesto mi přijde, že je dobré, pokud formulář o těch defaultních položkách při zpracování už ví (třeba je tam nechává, pokud uživatel dané pole nevyplní? teď si nejsem jistý). I David Grudl kdesi doporučil (koukám, že na ten příspěvek odkazuješ :)) dát plnění defaultních položek do továrničky či action fáze, asi to tedy nějaký důvod má :) Továrnička má nevýhodu v tom, že tam si musíš parametr aktuálního view vytáhnout pomocí $this->getParam('nazevParametru') a kdybys přitom formulář vykresloval i v nějakém view, kde žádný takový parametr nemáš (čemuž systém továrniček nijak nebrání), nedopadlo by to dobře.

Smysl action fáze – tam se prostě provádějí věci, které se musí před handlem provést. Můžeš tam mít nějaké nastavování atributů třídy, které pak v handleru využíváš, můžeš tam kontrolovat nějaká oprávnění a vyhodit případně BadRequestException výjimku s kódem 403, čímž zabráníš handleru a renderu ve spuštění. Action metodu sice nevyužiješ všude, ale svůj smysl má. Vymyšlené to je dobře :)

Martin
Člen | 171
+
0
-

Našel jsem to náhodou, ale myslím, že na odpovědi v tomto vláknu by skoro měl být odkaz z dokumentace. Nebo něco z toho už dnes neplatí?

Editoval Martin (10. 5. 2011 12:51)