Zjednodušení životního cyklu?
- David Grudl
- Nette Core | 8228
Během školení a vůbec tak nějak časem se mi zase zdá, že životní cyklus presenteru je příliš složitý a uživatelé pak opakují stále stejné chyby. Podobné úvahy vždycky skončily tím, že to prostě není jak zjednodušit, no ale víc hlav víc ví, takže téma otevírám veřejně.
Jedním z dosud nerealizovaných nápadů je továrnička na komponenty v presenteru (nebo komponentě). Komponenta se tvoří, až když je potřeba a odpadají výjimky kvůli inicializaci komponenty užívající signály až v části render (typicky AppForm). V podstatě je to řešitelné uživatelsky i nyní přepsáním metody __get(), případně getComponent(), tedy nebýt final. Tento koncept by mohl eliminovat metody „prepare“.
Se „signálovou“ metodou se dá manipulovat poměrně
dobře, ale stále nejsem spokojený s jejím názvem ve tvaru
handleXyz()
;)
Zbývají tak dvě hlavní metody action a render, což je nutné minimum a tady už není co zjednodušovat.
p.s. použil někdo někdy shutdown()?
- romansklenar
- Člen | 655
Mě to příjde docela srozumitelné, akorát je třeba respektovat ty pravidla (typu: „to musí být tam a to zase nesmí být tam“) na které se zapomíná, nebo o nich lidi neví, protože dokumentace k presenteru ještě nedávno nebyla. Nevím jestli je dokumentace životního cyklu k presenteru dobře napsaná (napsal jsem tam jen ty pravidla o kterých vím nebo jsem je vyčet na fóru), ale kdyby byla určitě by to pomohlo předejít těm chybám :)
Shutdown používám akorát v presenteru, který umožňuje kešování na
uložení obsahu do kešky a v ErrorPresenteru pro nastavení metody
Debug::exceptionHandler()
.
- phx
- Člen | 651
Kdyby by,o vse v jedne metode tak se to zacina blizit k linearnimu programovani obaleho objekty. Domnivam se, ze soucasna situace je fajn. Osobne bych nic nemenil. Nic me na tom netrapi a po pochopeni myslenky (co kde ma a nema byt) je vse OK.
Snad jen drobnost. Parametr se musi opakovat u vsech metod (prepare{view}, render{view}, … ?) Coz je obcas trochu zbytecne kdyz jej v dane metode nevyuziji.
Add shutdown(): zatim jsem ji nepouzil, ale nerusil bych ji:)
- kravčo
- Člen | 721
David Grudl napsal(a):
Jedním z dosud nerealizovaných nápadů je továrnička na komponenty v presenteru (nebo komponentě). Komponenta se tvoří, až když je potřeba a odpadají výjimky kvůli inicializaci komponenty užívající signály až v části render (typicky AppForm). V podstatě je to řešitelné uživatelsky i nyní přepsáním metody __get(), případně getComponent(), tedy nebýt final. Tento koncept by mohl eliminovat metody „prepare“.
to vyzerá veľmi zaujímavo…
ja som sa v používaní action*` & prepare*
metód riadil
pravidlom – čo treba spracovať pred presmerovaním →
present*
, čo stačí po ňom → prepare*
, ale
zväčša ostali aj tak metódy prepare*
prázdne, alebo
poloprázdne :)
Se „signálovou“ metodou se dá manipulovat poměrně dobře, ale stále nejsem spokojený s jejím názvem ve tvaru
handleXyz()
;)
kedysi
bolo viacero nápadov, mne akurát napadá processXYZ()
, čo
samozrejme koliduje minimálne s processSignal()
, ale napr.
processClick()
mi príde vcelku príjemný názov. Ponúkajúci sa
signalXYZ()
sa mi síce nezdá vhodný, ale možno niekto iný
príde s argumentom prečo by mohol byť…
p.s. použil někdo někdy shutdown()?
zatiaľ nie, ale čo ak náhodou? ;)
- jm
- Člen | 10
Az jsem pomoci dokumentace (diky!) porozumnel zivotnimu cyklu, tak mi prisel velmi dobry, protoze hezky oddeluje cinnosti. Obcas by se mi vsak hodilo zpracovat signaly (handle{Signal}) pred prepare{View}, ale neni to velky problem.
Jo a shutdown() jsem zrovna zacal pouzivat na serializaci pole objektu, kde potrebuji uchovat hodnoty behem session.
- pmg
- Člen | 372
Tomik napsal(a):
Osobně mi současný stav taky vyhovuje. Jinak co „getClick“? Get je anglicky i obstarat, obdržet, takže by to více-méně odpovídalo, ne?
Teoreticky asi ano, ale koliduje to se zažitou konvencí používat get pro gettery.
Toto sem už moc nepatří, ale poslechněte si to. Co se zmíněné metody týče, byl bych spíš pro to, aby Nette\Object při volání $class->getClick() vracel $class->click, a hlavně potom aby při volání $class->setClick($value) nastavil $class->click = $value a pak vrátil samotnou třídu, aby to šlo řetězit. Prostě přidat opak toho, co umí teď. Dal by se pak používat setter na hodnoty, které ho zatím nepotřebují (a jsou public). Když by pak setter potřeba byl, nemusel by se předělávat kód.
- David Grudl
- Nette Core | 8228
pmg napsal(a):
Toto sem už moc nepatří, ale poslechněte si to. Co se zmíněné metody týče, byl bych spíš pro to, aby Nette\Object při volání $class->getClick() vracel $class->click, a hlavně potom aby při volání $class->setClick($value) nastavil $class->click = $value a pak vrátil samotnou třídu, aby to šlo řetězit.
To není špatný nápad. Znamenalo by to ale udělat z fluent setter standard, tj. všechny settery by musely vracet $this.
Dal by se pak používat setter na hodnoty, které ho zatím nepotřebují (a jsou public). Když by pak setter potřeba byl, nemusel by se předělávat kód.
Logika je spíš taková, že pokud používáš
$class->click
, kde click je public proměnná, tak nemusíš
předělávat kód, když se z toho stane getter. Public proměnné jsou
vlastně jen zjednodušením pro triviální setter/getter, který jen vrací a
nastavuje proměnnou.
- David Grudl
- Nette Core | 8228
phx napsal(a):
Snad jen drobnost. Parametr se musi opakovat u vsech metod (prepare{view}, render{view}, … ?) Coz je obcas trochu zbytecne kdyz jej v dane metode nevyuziji.
Add shutdown(): zatim jsem ji nepouzil, ale nerusil bych ji:)
No, pokud se nevyužije, opakovat se nemusí, dokonce mohou být uvedeny různé parametry v různém pořadí. Klíčová je ale metoda, podle které se tvoří odkaz (link). A tou je buď action* nebo render*.
- A.
- Člen | 87
David Grudl napsal(a):
To není špatný nápad. Znamenalo by to ale udělat z fluent setter standard, tj. všechny settery by musely vracet $this.
Spis by bylo vhodne vratit to, co vrati patricny setter, je pak uz na kazdem, zda-li si vraci $this, popr. nasetovanou hodnotu, coz take muze byt potreba.
Co se tyce nazvu metod na zpracovani signalu, handleXYZ mi prijde zatim opravdu nejlepsi pojmenovani ze vsech variant, ktere byly navrzeny (naopak navrhovane nazvy jsou mnohonasobne horsi). Navic, „handle“ ten vyznam opravdu vystihuje, nemenil bych.
- pmg
- Člen | 372
David Grudl napsal(a):
Logika je spíš taková, že pokud používáš$class->click
, kde click je public proměnná, tak nemusíš předělávat kód, když se z toho stane getter. Public proměnné jsou vlastně jen zjednodušením pro triviální setter/getter, který jen vrací a nastavuje proměnnou.
Jasně, občas bývám náměsíčnej. Funguje to tak už i teď, tak asi nemá cenu to komplikovat. Leda že by výhoda atomatického fluent interface převážila nejednotnost syntaxe.
A. napsal(a):
Spis by bylo vhodne vratit to, co vrati patricny setter, je pak uz na kazdem, zda-li si vraci $this, popr. nasetovanou hodnotu, coz take muze byt potreba.
Nette\Object se dostane ke slovu až tehdy, když je zavolán neexistující setter (setValue). Potom se může podívat, jestli ve třídě není nějaká proměnná ($value) s přístupem public a případně ji nastavit na předanou hodnotu. Když do třídy ten setter doplníš, už si můžeš vrátit, co chceš.
Edit: Ještě jedna věc. Automatický setter by mohl kontrolovat typ uvedený v anotaci @param.
Editoval pmg (19. 11. 2008 17:24)
- Honza Marek
- Člen | 1664
To není špatný nápad. Znamenalo by to ale udělat z fluent setter standard, tj. všechny settery by musely vracet $this.
To by bylo pěkné.
- David Grudl
- Nette Core | 8228
Houstone, máme problém!
Při úpravách jsem narazil na problém se značením persistentních komponent. Není to úplně domyšlené. Nette dokáže rozeznat, jestli jde o komponentu nebo obyčejnou proměnnou, teprve ve chvíli, kdy se do té proměnné komponenta uloží. Což znamená, že pokud provedu např. redirect dřív, než komponentu vytvořím, její persistence se ztratí.
Jediné řešení vidím ve změně značení persistentních komponent:
/**
* @persistent(game, abc, xyz)
*/
class DefaultPresenter extends Presenter
{
public function prepareDefault()
{
$fifteen = new FifteenControl($this, 'game');
$fifteen->onGameOver[] = array($this, 'GameOver');
$this->template->fifteen = $fifteen;
}
...
Navíc by tak odpadla potřeba komponenty vůbec do proměnných objektů ukládat.
Zároveň by možná nebylo od věci přidat do Nette obecnější mechanismus na parsování anotací v komentářích.
- phx
- Člen | 651
Persistentni komponenty jsem zatim nepouzil, ale pokud to chapu dobre tak ted je to takto:
class MujPresenter extends Presenter {
/** @persistent */
public $komponenta;
public function prepareDefault() {
$this->komponenta = new FifteenControl($this, 'game');
// atd...
}
}
Osobne bych se drzel anotaci vzdy u prvku, ktereho se to tyka! Obecne
u persistence by mozna stalo za to rozsirit aby se z anotace bral typ. Pokud
chci aby vychozi hodnota byla NULL a polovene hodnoty pouze INT tak to
neudelam:( Predstava by byla: /** @persistent int */
Tim by take
slo udelat napriklad i /** @persistent FifteenControl */
a problem
by se mel odstranit ne?
A nebo jsem to nepochopil:)
Editoval phx (20. 11. 2008 10:43)
- David Grudl
- Nette Core | 8228
Spíš by asi bylo lepší
/** @var FifteenControl @persistent */
, nicméně stále chybí
vazba mezi identifikátorem game
a $komponenta
. Navíc
je nutno ověřit, že typ FifteenControl implementuje IStatePersistent.
Zcela souhlasím, že anotace se mají držet prvku, kterého se týkají. A právě anotace u hlavičky třídy tohle IMHO naplňuje lépe, než anotace u proměnné. Výčet persistentních položek je charakteristická vlastnost presenteru.
- kravčo
- Člen | 721
Nebolo by v tom prípade lepšie zvoliť ukecanejšiu anotáciu a dať do nej ako perzistentné komponenty, tak aj perzistentné premenné?
napríklad
/**
* @persistent (
* string $lang,
* component game,
* something else,
* )
*/
class NewNCoolPersistentPresenter extends Presenter {
public $lang;
public $fifteen; // tu bude uložený komponent s id „game“
// funkcie ...
}
alebo iný formát:
/**
* @persistent (
* @var $lang,
* @component game,
* @something else,
* )
*/
Editoval kravco (20. 11. 2008 17:44)
- veena
- Člen | 98
David Grudl napsal(a):
Zároveň by možná nebylo od věci přidat do Nette obecnější mechanismus na parsování anotací v komentářích.
Už jsem ti ukazoval dar přítele johna? http://code.google.com/p/addendum/
- Tomik
- Nette Evangelist | 485
Už jsem ti ukazoval dar přítele johna? http://code.google.com/p/addendum/
To vypadá pěkně! :)
- David Grudl
- Nette Core | 8228
Dokončil jsem poměrně dost náročné přepracování klíčových funkcí Presenteru, což by mělo v důsledku vést ke zjednodušení (respektive k rozšíření o nové možnosti a vyřešení nedostatků) životního cyklu.
Úpravy by neměly způsobit žádné problémy, jsou zpětně kompatibilní, kromě zmíněného značení persistentních komponent. To prosím předělejte dle návrhu. Komponenty označené starým způsobem vyvolají výjimku InvalidStateException: Persistent parameter must be scalar or array, ‚DefaultPresenter::$xyz‘ is object.
Než se úprava dostane do distribuce, je potřeba ji důkladně otestovat. Stáhněte si proto prosím tuto extra distribuci a vyzkoušejte, jestli vše funguje v pořádku.
Zároveň jsem trošku upravil příklad akrabat.forms, kde je vidět v činnosti továrnička na komponenty. Díky tomu je celá aplikace postavená pouze na metodách render*.
- romansklenar
- Člen | 655
Takže zběžně co jsem se na to díval jsem to pochopil asi takto: (kdyžtak mě opravte)
- s komponentami se manipuluje a uchovávají se nyní v
ComponentContainer
a až když je zavolánogetComponent('komponenta')
tak se komponenta vytvoří továrničkou tedy až když je doopravdy třeba - před vykreslovací fází už se neukládájí presenterem vnitřní stavy
komponent (metoda
saveState
byla z presenteru odstraněna) a je to v plné režijiPresenterComponent
(který ji jako rodič implementuje) - vnitřně se nějakým způsobem změnila práce s odkazy, metoda
createSubRequest
už nebude využívána, byla odstraněna - jaký je rozdíl mezi vytvořením komponenty továrničkou v action{Action} a render{View} a co je na jaké případy vhodnější?
Zatím to vypadá, že metody životního cyklu presenteru
jsou zachovány, plánuje se ještě eliminovat metody prepare
jak
je zmíněno v úvodním postu?
Editoval romansklenar (21. 11. 2008 5:23)
- romansklenar
- Člen | 655
Ikdyž si změníš stránkování fóra po 30 příspěvcích aby se ti to vlezlo na jednu stránku, pořád to bude směrovat na tvůj příspěvek, takže asi překlep :)
Editoval romansklenar (21. 11. 2008 8:28)
- David Grudl
- Nette Core | 8228
romansklenar napsal(a):
Takže zběžně co jsem se na to díval jsem to pochopil asi takto: (kdyžtak mě opravte)
- s komponentami se manipuluje a uchovávají se nyní v
ComponentContainer
a až když je zavolánogetComponent('komponenta')
tak se komponenta vytvoří továrničkou tedy až když je doopravdy třeba
Ano, o továrničku je rozšířena třída ComponentContainer. Jinak tady se nic víc nemění. Díky tomu i jednotlivé komponenty (např. PresenterComponent) mohou používat továrničky.
- před vykreslovací fází už se neukládájí presenterem vnitřní stavy komponent (metoda
saveState
byla z presenteru odstraněna) a je to v plné režijiPresenterComponent
(který ji jako rodič implementuje)
Ano, ukládají se po vykreslovací fázi. Ono ukládání je spíš záležitost výkonnostní optimalizace, nemělo by to mít na nic vliv.
- jaký je rozdíl mezi vytvořením komponenty továrničkou v action{Action} a render{View} a co je na jaké případy vhodnější?
Výhoda továrničky je v tom, že signal handler si může komponentu (nebo subkomponentu) „vytáhnout“ bez toho, že by se musela dopředu inicializovat v action nebo prepare.
Zatím to vypadá, že metody životního cyklu presenteru jsou zachovány, plánuje se ještě eliminovat metody
prepare
jak je zmíněno v úvodním postu?
Jasně, zpětná kompatibilita je zachována, jen se snažím hledat nové cesty.
- David Grudl
- Nette Core | 8228
Chyby opraveny.
Tak jsem to pustil do ostré distibuce a budu se modlit ;)
- David Grudl
- Nette Core | 8228
veena napsal(a):
Už jsem ti ukazoval dar přítele johna? http://code.google.com/p/addendum/
Jojo, dar přítele Johna vypadá velmi dobře. Pokusil jsem se do Nette přidat vlastní implementaci – Nette\Annotations
- johno
- Člen | 10
David Grudl napsal(a):
Jojo, dar přítele Johna vypadá velmi dobře. Pokusil jsem se do Nette přidat vlastní implementaci – Nette\Annotations
Smrk!