Persistentni komponenty – nejak to nechapu :(
- jasir
- Člen | 746
Zdravím,
s nette začínám a nějak zřejmě nechápu „persistentní
komponenty“. :)
Nejprve kód
<?php
class MujPresenter extends Presenter
{
/** @persistent */
public $mojekomponenta;
public actionDefault()
{
$this->mojekomponenta = new MojeKomponenta();
}
public handleAction1()
{
...zmeny dat v $mojekomponenta...
}
public handleAction2()
{
...zmeny dat v $mojekomponenta
}
public handleSave()
{
...ulozi se zmeny...
}
}
class MojeKomponenta extends Control
{
private $mojeRozsahlaData;
private $data; //pole objektu
public function __construct($parent,$name)
{
parent::__contstruct($parent,$name);
}
public function pripravKomplikovanaData()
{
...sahej do databaze a delej kouzla...
}
}
?>
Chování, kterého chci dosáhnout:
- když se poprvé zobrazuje prezenter, inicializuje se moje komponenta
(provede nějaké
časově náročné operace, natáhne data z databáze. Tato komponenta (=objekt) zůstane perzistentní
(tj. při každém dalším načtení stránky se uchovávají dosavadní změny)
- dále se v prezenteru „edituje“ obsah komponenty – v signálech
handleXXX se postupně
upravují data v komponentě
- nakonec se při handleSave() komponenta „uloží co v sobě má“
Bohužel, zřejmě jsem to nepochopil (ačkoliv v dokumentaci je příklad
z Fifteen – který se ale
v aktuální distribuci změnil komponenta $fifteen už není označena jako
@persistent). Co se stane je, že se vyhodí exception, že
$mojeKomponenta není array or scalar. Persistent parameter must be scalar or
array, ‚Survey_QuestionModelDevelopmentPresenter::$question‘ is object.
Snad je to jasné, kdyžtak objasním.
Díky moc za rady.
ps. jinak nette mi připadá úplně super, obrovský dík Davidovi za ohromný kus práce
- jasir
- Člen | 746
Okej, zřejmě je moje otázka příliš zmatená. Strašně dlouho jsem to včera zkoušel horem/dolem, protože dokumentace je v této oblasti řekl bych hodně slabá :( Takže po vrtání v zdrojácích Nette a hledání ve fóru jsem dospěl k následujícímu zjištění, můzete mi to prosím potvrdit, vyvrátit?
1) Presenter
Komponenta jako celek nemůže být perzistentní, pouze v komponentě deklarované proměnné mohou být persistentní.
V presenteru se persistentní komponenta nijak ve zdrojáku neoznačuje
a ani nedeklaruje, viz. příklad fifteen, pouze se v metode
action()
vytvoří – new mojeKomponenta()
a vloží do presenteru
pomocí addComponent($mojeKomponenta,'jmenokomponenty')
2) Komponenta
V komponentě se označují perzistentní proměnné takto
<?php
class MojeKomponenta extends Control {
/** @persistent int */
public $Id;
/** @persistent array */
public $prvky;
/** @persistent bool */
public isLoaded;
/** @peristent DibiTable */
public $data;
}
?>
Bohužel jiné proměnné jiného typu než int, array a string a boolean
vyhodí exception.
Zde se pro příklad snažím persistentně udržet instanci DibiTable
3) Persistence objektu saveState(), loadState()
Pokoušel jsem se tedy docílit persistence typu objekt, zřejmě
přetížím tyto metody, podobně jako
v příkladu fifteen (kde se ale manipuluje pouze s array).
Pokoušel jsem se upravit metody loadState(), saveState(), ale bez
úspěchu:
<?php
/**
* Loads params
* @param array
* @return void
*/
public function loadState(array $params)
{
echo "loadstate params<br>";
Debug::dump($params);
if (isset($params['data'])) {
$params['data'] = unserialize( (string) $params['data']) ;
}
parent::loadState($params);
}
/**
* Save params
* @param array
* @return void
*/
public function saveState(array & $params)
{
echo "savestate:params<br>";
Debug::dump($params);
parent::saveState($params);
if (isset($params['data'])) {
$params['data'] = serialize($params['data']);
}
}
?>
To ale samozřejmě nefunguje.
4) Kdy se děje loadState()?
Chci docílit chování, kdy presenter, když je poprvé načten inicializuje
komponentu,
ta si natáhne data z datábaze (do @persistent $data). Při dalším
požadavku (subrequestu,
signálu) už komponenta data z databáze netahá, ale má je uložené
perzistentně.
Protože loadState() proběhne až po metodách actionXXX() (viz. příklad
fifteen),
komponenta se vždy znovu inicializuje z databáze (persistentní proměnná
isLoaded není jestě nahraná, takže nemohu použít v komponentě
<php
…
if(!$this->isLoaded)
$this->fetchDataFromDatabase()
…
?>
5) Úložiště
Úložiště pro persistentní proměnné je pouze URI – no prostě
v URL. Nevím, jestli je nějaké
omezení na délku URL, ale nebylo by lepší do url ukládat pouze nějaký
identifikátor a perzistentní
proměnné pak tahat z něj? Například zde v mém příkladu potřebuji mít
perzistentní $data a řekněme, že to může být 1000 řádků
z databáze…
No, je to dost dlouhý příspěvek, snad to někdo přečte… a poradí… Děkuju
- Mas3r
- Člen | 116
jasir napsal(a):
Chci docílit chování, kdy presenter, když je poprvé načten inicializuje komponentu,
ta si natáhne data z datábaze (do @persistent $data). Při dalším požadavku (subrequestu,
signálu) už komponenta data z databáze netahá, ale má je uložené perzistentně.Úložiště pro persistentní proměnné je pouze URI
Právě sis odpověděl sám. Persistetní můžou být jen parametry a to parametry typu INT a STRING ( možná serializovaný objekt, ale to je vlastně taky string, ne? ). Pokud chceš data z DB uchovávat pro snížení počtu dotazů, tak použij Cache.
O cache najdeš více v dokumentaci.
- jasir
- Člen | 746
Mas3r napsal(a):
jasir napsal(a):
Chci docílit chování, kdy presenter, když je poprvé načten inicializuje komponentu,
ta si natáhne data z datábaze (do @persistent $data). Při dalším požadavku (subrequestu,
signálu) už komponenta data z databáze netahá, ale má je uložené perzistentně.Úložiště pro persistentní proměnné je pouze URI
Právě sis odpověděl sám. Persistetní můžou být jen parametry a to parametry typu INT a STRING ( možná serializovaný objekt, ale to je vlastně taky string, ne? ). Pokud chceš data z DB uchovávat pro snížení počtu dotazů, tak použij Cache.
O cache najdeš více v dokumentaci.
No Cache je sice prima, ale neřeší to můj problém (nebo nevim). Já
chci, aby komponenta MujKontrol, dokud existuje (tj. je v nějakém prezenteru)
měla uložena data z datábaze.
Když se prezenter změní, a pak se znovu načte tento prezenter, v Cache už
data být nemají a mají se načíst znovu. Podle mě by bylo nejlepší, kdyby
se při definování persistent dalo určit, zda mají být
uloženy na serveru (cache, session…?) nebo v uri, objekty by se automaticky
ukládali na serveru.
Při změně prezenteru by se pak buď přenášeli (pokud by ve dvou
prezenterech byly definované stejné persistent) a hlavně sami mazaly.
Třeba nějak takto:
<?php
class MujControl extends Control
{
/** @persistent (object, server) */ //tato se uloží na serveru, je typu object
public $data
/** @persistent int */
public $id; //standartně přes URI
}
?>
- Ondrej
- Člen | 110
jasir napsal(a):
- Kdy se děje loadState()?
Podle mě by bylo nejlepší, kdyby se při definování persistent dalo určit, zda mají být
uloženy na serveru (cache, session…?) nebo v uri
loadState() se deje pri pripojeni controlu k presenteru. Obvykle
v konstruktoru. Metodu loadState si muzes
definovat svoji, muze treba data nacitat ze session a v saveState() ukladat
data do session.
Resil bych to nejak takto:
<?php
class MujControl extends Control
{
/** @persistent int */
public $id; //ID objektu
/** normalni attribut **/
private $data
function loadState(array $params)
{
parent::loadState($params);
$this->data = getDataFromSession($params['id']);
}
}
?>
Editoval Ondrej (21. 1. 2009 14:06)
- jasir
- Člen | 746
Ondrej napsal(a):
jasir napsal(a):
- Kdy se děje loadState()?
Podle mě by bylo nejlepší, kdyby se při definování persistent dalo určit, zda mají být
uloženy na serveru (cache, session…?) nebo v uriloadState() se deje pri pripojeni controlu k presenteru. Obvykle v konstruktoru. Metodu loadState si muzes
Aha!!! Tak to je jeda z mych chyb proc mi to nefungovalo. Volal jsem
‚$mojeKomponenta->loadData()‘ pred ‚addComponent()‘, čili se
nanatáhly ty data a pořád
se sosalo z databáze. (obšlehl jsem to z fifteen) Díky
definovat svoji, muze treba data nacitat ze session a v saveState() ukladat data do session.
Resil bych to nejak takto:
<?php class MujControl extends Control { /** @persistent int */ public $id; //ID objektu /** normalni attribut **/ private $data function loadState(array $params) { parent::loadState($params); $this->data = getDataFromSession($params['id']); } } ?>
Jo, takhle to vypadá logicky, vyzkouším, díky moc!
Jenom – šlo by to ještě mě trochu nakopnout, jak zařídit chování, aby
když opustím prezenter s komponentou a vrátím
se pak na něj zpatky, se data znova načetli i když jsou někde
v cache/session? Nebo nemá prezenter nějaké vlastní úložiště
cachovaných dat, které se automaticky invaliduje při opuštění
prezenteru?
Jinak to značení perzistentních proměnných a vkládání perzistentních proměnných(viz body 1 a 2 jsou správně?)
Editoval jasir (21. 1. 2009 15:28)
- Ondrej
- Člen | 110
jasir napsal(a):
se pak na něj zpatky, se data znova načetli i když jsou někde v cache/session? Nebo nemá prezenter nějaké vlastní úložiště cachovaných dat, které se automaticky invaliduje při opuštění prezenteru?
to asi nepujde, nevim, jak mas promyslene takove chovani pokud budu mit
aplikaci otevrenou ve dvou oknech zaroven.
Reseni je invalidovat pri nacteni jineho presenteru. Proste si nejak ukladat
posledni nacteny presenter a pokud se posledni != aktualni, tak data
invalidovat.
{EDIT}
persistentni parametr $id pri opetovnem vstupu na presenter je prazdny, takze
control stejne nevi odkud ma data nacist, to by mohlo tvuj pridat resit
{/EDIT}
Editoval Ondrej (21. 1. 2009 16:31)
- jasir
- Člen | 746
Ondrej napsal(a):
jasir napsal(a):
se pak na něj zpatky, se data znova načetli i když jsou někde v cache/session? Nebo nemá prezenter nějaké vlastní úložiště cachovaných dat, které se automaticky invaliduje při opuštění prezenteru?
to asi nepujde, nevim, jak mas promyslene takove chovani pokud budu mit aplikaci otevrenou ve dvou oknech zaroven.
Reseni je invalidovat pri nacteni jineho presenteru. Proste si nejak ukladat posledni nacteny presenter a pokud se posledni != aktualni, tak data invalidovat.{EDIT}
persistentni parametr $id pri opetovnem vstupu na presenter je prazdny, takze control stejne nevi odkud ma data nacist, to by mohlo tvuj pridat resit
{/EDIT}
Ok, to zní taky docela logicky… Zkusím si s tím pohrát. Myslíš, že je ještě nějaká jiná cesta jak zjistit, že se prezenter načítá poprvé?
- phx
- Člen | 651
Persistentni muze byt jen INT a STRING. Pokud chces kesovat cely objekt tak jedine Cache. Uz to tu zaznelo.
Jinak pokud chces persistentnit komponentu (jeji persistentni parametry) tak musis jeste komponentu oznacit jako persistentni.
/** @persistent(jmeno) */
class BasePresenter extends Presenter {
protected function createComponentJmeno($name) {
return new MyControl;
}
}
Je tu nejaky vlakno na foru o tom, ale nemuzu to najit:(
- David Grudl
- Nette Core | 8218
Možná je snadnější chápat persistentní parametry jako parametry, které není třeba uvádět při odkazování metodou link(), viz https://forum.nette.org/…iewtopic.php?….
- kravčo
- Člen | 721
phx napsal(a):
Persistentni muze byt jen INT a STRING. Pokud chces kesovat cely objekt tak jedine Cache. Uz to tu zaznelo.
Jinak pokud chces persistentnit komponentu (jeji persistentni parametry) tak musis jeste komponentu oznacit jako persistentni.
Znamená to, že komponent ako celok perzistentný byť nemôže, perzistentné môžu byť len niektoré (aj všetky) jeho premenné? Perzistentné v zmysle, že ich netreba zadávať do URL a prenášajú sa automaticky…
/** @persistent(jmeno) */ class BasePresenter extends Presenter { protected function createComponentJmeno($name) { return new MyControl; } }
Je tu nejaky vlakno na foru o tom, ale nemuzu to najit:(
Zrejme si myslel to o novom značení perzistentných komponentov.
- David Grudl
- Nette Core | 8218
Persistentní mohou být i komponenty, jen se ve zdrojovém kódu označují jinak.