Zjednodušení životního cyklu?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
David Grudl
Nette Core | 7790
+
0
-

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
+
0
-

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().

Jod
Člen | 701
+
0
-

No keby sa na action, prepare, render používala jedna metóda by nebolo zlé. Niektoré veci niekedy neviem kam mám vopchať :D . Polial nemám nejaký control tak všetko aj tak pchám do render.
Fakt je to asi trochu matúce.

phx
Člen | 651
+
0
-

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
+
0
-

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
+
0
-

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.

Tomik
Nette Evangelist | 485
+
0
-

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?

pmg
Člen | 372
+
0
-

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 | 7790
+
0
-

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 | 7790
+
0
-

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
+
0
-

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.

Jod
Člen | 701
+
0
-

Jj handle je fajn :)

pmg
Člen | 372
+
0
-

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
+
0
-

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é.

Jod
Člen | 701
+
0
-

A videli ste už settery vracať hodnoty v iných programovacích jazykoch? Nevravím zas, žeby to bolo zlé =)

Honza Marek
Člen | 1664
+
0
-

Já to viděl v jQuery.

Jod
Člen | 701
+
0
-

Chcel som povedať iných objektových programovacích jazykoch, keďže ten setter je objektová fičurda a php sa snaží byť objektové, sem tam :D :D

David Grudl
Nette Core | 7790
+
0
-

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
+
0
-

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 | 7790
+
0
-

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.

phx
Člen | 651
+
0
-

Takze nejde o persistenti atribut presenteru, ale o nazev komponenty. Nejak mi unika proc by komponenta mela byt persistentni, kdyz neni v persistentni promenne? Asi jsem to nepochopil, nebo nevidim onen problem.

kravčo
Člen | 721
+
0
-

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
+
0
-

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
+
0
-

Už jsem ti ukazoval dar přítele johna? http://code.google.com/p/addendum/

To vypadá pěkně! :)

David Grudl
Nette Core | 7790
+
0
-

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
+
0
-

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áno getComponent('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žiji PresenterComponent (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)

phx
Člen | 651
+
0
-

David Grudl napsal(a):

To prosím předělejte dle návrhu.

Neuklepnul jsi se v odkazu?

EDIT: Ha BUG, na 2. strane nefunguji kotvy:(

Editoval phx (21. 11. 2008 8:25)

romansklenar
Člen | 655
+
0
-

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 | 7790
+
0
-

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áno getComponent('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žiji PresenterComponent (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.

LM
Člen | 206
+
0
-

Signály nefungují (na presenteru, v komponentách ano) končí to na: BadSignalException: The signal receiver component '' is not found.

David Grudl
Nette Core | 7790
+
0
-

Chyby opraveny.

Tak jsem to pustil do ostré distibuce a budu se modlit ;)

David Grudl
Nette Core | 7790
+
0
-

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
+
0
-

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!