Persistentni komponenty – nejak to nechapu :(

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

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

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

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

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

jasir napsal(a):

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

Ondrej napsal(a):

jasir napsal(a):

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

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

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

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

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

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

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

Persistentní mohou být i komponenty, jen se ve zdrojovém kódu označují jinak.

jasir
Člen | 746
+
0
-

Díky moc, teď je mi to už jasné. (Ale dalo to fušku :-)) Nakonec jsem od myšlenky ukládat si celé objekt upustil, zřejmě si zatím vystačím s array. Nette se teprve učím, ale každý další kousek skládanky je lepší a lepší!