Jak na model v Nette?

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

Zdravim,

na úvod bych chtěl říct, že s Nette teprve začínám :) Nicméně bych se rád zeptal, jak kdo řešíte komunikaci s modelem? V quickstartu jsem si všiml, že reference na db connect je uložena jako vlastnost BasePresenteru. Ale teda nevím, jestli je to přesně to, co bych čekal. Čekal bych spíš nějaké extra třídy s veřejným rozhraním getrů a setrů. Takhle přistupovat přímo do db, ikdyž přes bezva dibi, mi nepřijde ideální.

Můžete se podělit o své zkušenosti případně implementace?

Snad jsem to formuloval dost jasně, přeci jen, jsem z toho krapet zmatený ;)

Díky moc.

Honza Marek
Člen | 1664
+
0
-

Podle mě Nette model nijak zvlášť neřeší. Nejlepší asi bude, když si vyrobíš v app složku models a tam budeš dávat soubory s třídami modelu podle vlastních potřeb. Mohou to být třeba i rozšířené třídy DibiTable.

No a pak v presenteru můžeš volat např. new Model i bez require, protože pokud prochází RobotLoader složku app, projde i app/models.

Villem
Člen | 19
+
0
-

Model, pokud jej vyžaduješ, by měl obsahovat/implementovat pouze tzv. aplikační logiku. Tedy manipulaci, získávání a ukládání dat. Rozhraní getrů a setrů je jenom jedna možnost implementace.

V tomto konkrétním případě (quickstart) by navíc daný případný model jenom předával neupravené výstupy z dibi dál, takže bys jenom zbytečně tvořil jakýsi adaptér, aniž bys jej potřeboval (např. z důvodu typové kontroly).

Jod
Člen | 701
+
0
-

Čav, toto som sa chcel aj ja už dlhšiu dobu opýtať, ako to kto robí.
Ja to robím takto:

Mám adresár app/models kde mám modely.
Každý model patrí k jednému modulu, resp. presenteru.
Takže modul news sa volá NewsPresenter (prípadne NewsModule/DefaultPresenter).
Rovnako nazvem aj modul podla názvu, takže NewsModule.
NewsModule bude obsahovať hlavne metódy na prácu s databázov a ďalšie logické.
Pre zoznam noviniek si napríklad spravím metódu getAllNews, ktorú budem volať v presenteru aby mi vrátila všetky novinky.
V presenteru v startup si vytvorím inštanciu modulu.

<?php
function startup()
{
	$this->module = new NewsModule();
}
?>

A potom to normálne využívam v presenteri:

<?php
function actionDefault()
{
	$news = $this->module->getAllNews();
}
?>

Neviem ako to robia ostatní ale mne sa to zdá najlogickejšie, ak to niekto rieši lepšie, mohol by sa s nami podeliť :)

Jakub Šulák
Člen | 222
+
0
-

Nebylo by lepší takovéto metody mít jako statické a volat je NewsModule::getAllNews()?

Jod
Člen | 701
+
0
-

Ano otom som rozmýšlal. dibi disponuje tým statickým oným (zabudol somako sa to volá dibi::) :D , ale vezmime si napríklad že tam chceme udržiavať connection, alebo nejaké data. Potom statické volanie neni moc výhra.

David Grudl
Nette Core | 8162
+
0
-

Jod napsal(a):

Neviem ako to robia ostatní ale mne sa to zdá najlogickejšie, ak to niekto rieši lepšie, mohol by sa s nami podeliť :)

Dělám to v podstatě takto.

Ondřej Brejla
Člen | 746
+
0
-

Villem napsal(a):

Model, pokud jej vyžaduješ, by měl obsahovat/implementovat pouze tzv. aplikační logiku. Tedy manipulaci, získávání a ukládání dat. Rozhraní getrů a setrů je jenom jedna možnost implementace.

V tomto konkrétním případě (quickstart) by navíc daný případný model jenom předával neupravené výstupy z dibi dál, takže bys jenom zbytečně tvořil jakýsi adaptér, aniž bys jej potřeboval (např. z důvodu typové kontroly).

V tom „co“ je model a co má dělat mám jasno. Uváděl jsem setry a getry právě jako implementační příklad, který obecně používám. Šlo mi spíš o to, kam v nette „modely“ ukládat, případně jaké kdo používá techniky.

David Grudl napsal(a):

Jod napsal(a):

Neviem ako to robia ostatní ale mne sa to zdá najlogickejšie, ak to niekto rieši lepšie, mohol by sa s nami podeliť :)

Dělám to v podstatě takto.

A toto mi v podstatě vše zodpovědělo. Přesně něco takového jsem si představoval :)

Teď by mě už opravdu jen zajímalo, jak to řeší ostatní…má tu někdo nějaký sofistikovanější přístup? :) Samozřejmě který by nám vysvětlil a shrnul případné výhody a nevýhody ;)

Zatím všem díky za odpovědi.

kravčo
Člen | 721
+
0
-

Aj keď to zatiaľ mám len v hlave, podľa toho, čo bolo napísané pri novinke {cache …} v curlybrackets filtri, mi pripadá lepšie lazy riešenie:

class NewsPresenter extends MyBasePresenter
{

    function startup()
    {
        $this->module = new NewsModule;
    }

    function renderDefault()
    {
        $this->template->news = $this->module;
    }

}
<body>
    {cache 'news/all'}
    {$news->getAllNews()} <!-- až toto spúšťa dopyt na databázu -->
    {/cache}
</body>

aj keď si nie som istý, či to nejde riešiť lepšie cez CacheableControl (prípadne niečo obdobné), ktorý tu pred časom postoval xificurk.

Tieto veci mám v pláne riešiť v blízkej budúcnosti, ak máte nejaké dobré hinty/faulty tak sa podeľte…

Jod
Člen | 701
+
0
-

Toto sa mi nejak nepáči. Najmä keď dáta z modelu predávaš nejakému kontrolu.

_Martin_
Generous Backer | 679
+
0
-

Jod napsal(a):

Toto sa mi nejak nepáči. Najmä keď dáta z modelu predávaš nejakému kontrolu.

Mě se to naopak líbí. Je to plně v souladu s popisem architektury MVP, resp. MVC. Pokud se model rozšíří o skupinu setterů, bude mít presenter možnost měnit model v závislosti na reakci uživatele, a view si getterem bude brát data, nestaraje se o nic jiného.

kravčo
Člen | 721
+
0
-

_Martin_ napsal(a):

Jod napsal(a):

Toto sa mi nejak nepáči. Najmä keď dáta z modelu predávaš nejakému kontrolu.

Mě se to naopak líbí. Je to plně v souladu s popisem architektury MVP, resp. MVC. Pokud se model rozšíří o skupinu setterů, bude mít presenter možnost měnit model v závislosti na reakci uživatele, a view si getterem bude brát data, nestaraje se o nic jiného.

Súhlas, myslel som to podobne.

Jod: toto riešenie nie je úplné, malo len načrtnúť myšlienku. Určite je v ňom potrebné vyriešiť to, aby do šablóny išiel model read-only, čiže napr:

class NewsPresenter extends MyBasePresenter
{
    // ...
    function renderDefault()
    {
        $this->module->readOnly = TRUE;
        $this->template->news = $this->module;
    }
}

A spraviť si objekt, ktorý sa môže rozhodnúť byť read-only:

class ReadonlyableObject extends /*Nette\*/Object
{
    protected $readOnly = FALSE;

    public function getReadOnly()
    {
        return $this->readOnly;
    }

    public function setReadOnly($value)
    {
        $this->readOnly = $value;
    }

    public function __set($name, $value)
    {
        if ($this->readOnly) {
            throw new Exception("Instance of ". get_class($this) ." is read-only.");
        }

        parent::__set($name, $value);
    }
}

A potom už len dediť…

kravčo
Člen | 721
+
0
-

kravco napsal(a):

class ReadonlyableObject extends /*Nette\*/Object
{
    protected $readOnly = FALSE;

    public function getReadOnly()
    {
        return $this->readOnly;
    }

    public function setReadOnly($value)
    {
        $this->readOnly = $value;
    }

    public function __set($name, $value)
    {
        if ($this->readOnly) {
            throw new Exception("Instance of ". get_class($this) ." is read-only.");
        }

        parent::__set($name, $value);
    }
}

Toto je ale dobrá blbosť, to ktorý expert vymyslel :D

Wosonj
Člen | 36
+
0
-

Myslím, že to není dobré řešení:

  • při jakékoliv chybě, třeba když ti spadne DB, jseš už v šabloně a tedy na konci životního cyklu presenteru. Chybu ošetříš jen obtížně a určitě ne transparentně.
  • jak už bylo řečeno výše, měnit model v šabloně je dost špatné řešení. Read only viz výše to řeší jen částečně – musely by se kontrolovat i všechny metody. A pak, co uděláš s výjimkou v šabloně?
  • html generuješ v modelu. To se vymstí hned jakmile budeš potřebovat to samé exportovat do RSS, poslat jako plaintext mailem nebo třeba někde jinde na webu zobrazovat jen nadpisy místo celé zprávy.

Myslím je praktičtější používat něco jako toto:

class NewsPresenter extends MyBasePresenter
{

    function startup()
    {
        $this->module = new NewsModule;
    }

    function renderDefault()
    {
        $this->template->news = $this->module->getAllNews();
        // vraci pole nebo pripadne nejaky primitivni objekt pro ulozeni dat
    }

}
<body>
    {cache 'news/all'}
    {foreach $news as $line}
    <h2>{$line->header}</h2>
    <p>{$line->text}</p>
    {/foreach}
    {/cache}
</body>
Jod
Člen | 701
+
0
-

Tak, tak, môj človek :)

_Martin_
Generous Backer | 679
+
0
-

Wosonj napsal(a):

  • při jakékoliv chybě, třeba když ti spadne DB, jseš už v šabloně a tedy na konci životního cyklu presenteru. Chybu ošetříš jen obtížně a určitě ne transparentně.

S tím by mohl být problém.

  • jak už bylo řečeno výše, měnit model v šabloně je dost špatné řešení. Read only viz výše to řeší jen částečně – musely by se kontrolovat i všechny metody. A pak, co uděláš s výjimkou v šabloně?

Zabránit změnám modelu z view by se nějakým způsobem zcela jistě dalo. Výjimka by pravděpodobně šla řešit bufferováním výstupu a jeho smazáním při zachycení výjimky.

  • html generuješ v modelu. To se vymstí hned jakmile budeš potřebovat to samé exportovat do RSS, poslat jako plaintext mailem nebo třeba někde jinde na webu zobrazovat jen nadpisy místo celé zprávy.

Nevím, jak přesně svůj kód myslel kravco, ale já jej pochopil takto:

<body>
    {cache 'news/all'}
    {foreach $news->getAllNews() as $line}
    <h2>{$line->header}</h2>
    <p>{$line->text}</p>
    {/foreach}
    {/cache}
</body>

Problém sem vnáší ono kešování – když posílám stránku z keše, pravděpodobně nebudu chtít, aby aplikace ještě tahala data z databáze a nějakým způsobem je zpracovávala. Vlastně mě to přivádí k otázce, kde začíná view. V šabloně? V metodě beforeRender?

kravčo
Člen | 721
+
0
-

Wosonj napsal(a):

  • při jakékoliv chybě, třeba když ti spadne DB, jseš už v šabloně a tedy na konci životního cyklu presenteru. Chybu ošetříš jen obtížně a určitě ne transparentně.

Áno, tu vidím problém.

  • jak už bylo řečeno výše, měnit model v šabloně je dost špatné řešení. Read only viz výše to řeší jen částečně – musely by se kontrolovat i všechny metody. A pak, co uděláš s výjimkou v šabloně?

Toto vie vyriešiť output buffering… to, že read-only vyššie nebude fungovať som pochopil ešte včera…

  • html generuješ v modelu. To se vymstí hned jakmile budeš potřebovat to samé exportovat do RSS, poslat jako plaintext mailem nebo třeba někde jinde na webu zobrazovat jen nadpisy místo celé zprávy.

Nie. Predpokladám totiž, že metóda Model::getAllNews() mi vráti celý control, ktorý sa už len vykreslí (pomocou svojej šablóny). Snažím sa v šablónach vyhýbať akémukoľvek kódu (aj foreach), ak to ide…

Problém sem vnáší ono kešování – když posílám stránku z keše, pravděpodobně nebudu chtít, aby aplikace ještě tahala data z databáze a nějakým způsobem je zpracovávala. Vlastně mě to přivádí k otázce, kde začíná view. V šabloně? V metodě beforeRender?

Tomuto nerozumiem, snažil som sa o to, aby sa výstup kešoval – teda dáta z db sa ťahať nebudú, ak je stránka dostupná v keši…


Ako som písal, mám to len v hlave… snažím sa totiž prísť na lazy riešenie… asi to predsa len bude lepšie poriešiť Cacheable Controlom…

Editoval kravco (14. 1. 2009 9:52)

_Martin_
Generous Backer | 679
+
0
-

kravco napsal(a):

Problém sem vnáší ono kešování – když posílám stránku z keše, pravděpodobně nebudu chtít, aby aplikace ještě tahala data z databáze a nějakým způsobem je zpracovávala. Vlastně mě to přivádí k otázce, kde začíná view. V šabloně? V metodě beforeRender?

Tomuto nerozumiem, snažil som sa o to, aby sa výstup kešoval – teda dáta z db sa ťahať nebudú, ak je stránka dostupná v keši…

Však ano, ty to přesně tak zamýšlíš, ale Wosonj si toho nevšiml (jeho řešení tahá data nezávisle na keši), tohle nebyla reakce na tebe =)

Wosonj
Člen | 36
+
0
-

Jo jasne, kesovani jsem si nevsiml. V Nette jsem to sice cache jeste nikdy neresil, ale myslim bych cacheovani urcite implementoval na urovni presenteru, cili zjistim, jestli mam data v cache, pokud ano, poslu je rovnou na sablonu, pokud ne, spustim model, ziskam data, ulozim do cache a poslu na sablonu.

Ale implementacni detaily jsem neresil – tzn. treba jaky ma dopad na vykon cacheovani struktur misto retezcu. Kazdopadne vyhoda je zase ta, ze ta sama data z cache se pak daji pouzit i pro RSS apod.

Z meho pohledu ma cache smysl jen pro databazi nebo nejakou jinou externi akci – vetsina ostatniho (pokud to samozrejme neni aplikace na pocitani PI), ma imo minimalni rezii ve srovnani se samotnym MVC frameworkem a nevyplati se to cacheovat jinde nez na urovni webserveru / proxy.