Jak do komponenty s výpisem produktů přidat formulář pro vložení do košíku

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

Ahoj, moc prosím o pomoc, už si nevím rady. Zápasím s komponentami a už nevím kudy kam :)

V šabloně vypisuji seznam produktů. Protože stejný seznam vypisuji na více místech, udělal jsem si na to komponentu takto:

soubor ProductControl.php (ve složce components)

class ProductControl extends Control
{

    public function render($product)
    {

        $template = $this->template;
        $template->setFile(__DIR__ . '/Product.latte');

        // vložíme do šablony udaje o produktu
        $template->item = $product;

        // a vykreslíme ji
        $template->render();
    }
}
interface IProductControlFactory
{
    /** @return ProductControl */
    function create();
}

šablona ve stejné složce ProducControl.latte

<div class="product">
	<h2 class="product-name"><a href="{plink Product:default $item->id}" title="{$item->name}">{$item->name}</a></h2>
	....
</div

V Base presenteru mám:

protected function createComponentProduct()
    {

        $control = $this->productFactory->create();
        return $control;
    }

A v šaboně příslušeného presenteru vkládám:

{control product $product}

Je to moje první komponenta a funguje jak potřebuji.
Jenže jen do chvíle, než sem potřeboval do šablony komponenty přidat tlačítko „přidat do košíku“. Tady už jsem narazil. Procházel jsem různé návody, forum a vytvořil jsem toto:

Do stávající komponenty ProductControl.php jsem přidal:

use Nette\Application\UI\Multiplier;
...
	....
protected function createComponentShopForm()
   {
       return new Multiplier(function () {
           $form = new Form;
           $form->addHidden('count', 1);
           $form->addHidden('itemId', 15222);
           $form->addSubmit('send', 'Přidat do košíku');
           return $form;
       });
   }

a do šablony Product.latte:

<form n:name="shopForm">
	<button class="addtocart">
     <i class="fa fa-shopping-cart"></i>
     <span>Do košíku</span>
    </button>
</form>

Což mi vypíše chybu:
Argument 1 passed to Nette\Bridges\FormsLatte\Runtime::renderFormBegin() must be an instance of Nette\Forms\Form, instance of Nette\Application\UI\Multiplier given, called in \temp\cache\latte\app-components-ProductGrid.latte

Už nevím jak dál. Zároveň nevím, jak do formuláře předám ItemId – id produktu, aby se do košíku vložilo správné zboží (v příkladu je ve formuláři nějaké konkrétní ID natvrdo zadane.)

S Nette začínám, je to moje první komponenta, tak budu rád, když začátečníka navedete správným směrem.

CZechBoY
Člen | 3608
+
0
-

V tom formuláři vůbec nepotřebuješ políčko ID produktu – to ti totiž přijde z Multiplieru (když si ho tam dáš jako parametr té anonymní funkce). A předáš si ho v šabloně samozřejmě.

protected function createComponentShopForm()
   {
       return new Multiplier(function ($productID) {
           $form = new Form;
           $form->addHidden('count', 1);
           $form->addSubmit('send', 'Přidat do košíku');

			$form->onSuccess[] = function ($form, $values) use ($productID) { // use($productID) je potřeba kvůli anonymní funkci
				$this->model->addProductToCart($productID, 1 /* nebo $values->count */);
			};
           return $form;
       });
   }

pak v šabloně

{control product-$product->id} {* pomlčka je právě pro Multiplier *}

Editoval CZechBoY (24. 1. 2017 19:49)

andros
Člen | 145
+
0
-

prosím, prosím jako záčátečník s Nette se fakt ztrácím. Kam mám do šablony dát

{control product-$product->id} {* pomlčka je právě pro Multiplier *}

když v šabloně komponenty mám formulář takto:

<form n:name="shopForm">
    <button class="addtocart">
     <i class="fa fa-shopping-cart"></i>
     <span>Do košíku</span>
    </button>
</form>
CZechBoY
Člen | 3608
+
0
-

Tam jak si psal ten předchozí

{control product $product}

Takže asi v šabloně té komponenty? Když v ní vytváříš ten form. :-)

Editoval CZechBoY (24. 1. 2017 19:57)

andros
Člen | 145
+
0
-

CZechBoY napsal(a):

Tam jak si psal ten předchozí

{control product $product}

Takže asi v šabloně té komponenty? Když v ní vytváříš ten form. :-)

Tímto já volám v šabloně presenteru celou komponentu, tohle mi do šablony vepíše informace o jednom produktu.

{foreach $products as $product}

 {control product $product}

{/foreach}

V poli $products mám všechny produkty, které chci zobrazit, komponenta vypíše jeden konkrétní produkt

Opravil jsem to podle tebe, ale pořád dostávám chybu:

Argument 1 passed to Nette\Bridges\FormsLatte\Runtime::renderFormBegin() must be an instance of Nette\Forms\Form, instance of Nette\Application\UI\Multiplier given, called in \temp\cache\latte\app-components-ProductGrid.latte

Editoval andros (24. 1. 2017 20:12)

andros
Člen | 145
+
0
-

Pokud v šabloně komponenty vykreslím form pro přidání do košíku takto:

{control shopForm-$item->id}

všechno funguje jak má. Já ale potřebuju formulář vykreslit ručně:

<form  n:name="shopForm">
  <button class="addtocart">
   <i class="fa fa-shopping-cart"></i>
   <span>Do košíku</span>
   </button>
</form>

a v tu chvíli mi to řve:
Argument 1 passed to Nette\Bridges\FormsLatte\Runtime::renderFormBegin() must be an instance of Nette\Forms\Form, instance of Nette\Application\UI\Multiplier given,

Poraďte prosím, jak se s tím poprat.

andros
Člen | 145
+
0
-

Jak se zdá, tak stačilo ručně vykreslovaný formulář opravit na:

<form n:name="shopForm-$item->id">
                <button class="addtocart">
                    <i class="fa fa-shopping-cart"></i>
                    <span>Do košíku</span>
                </button>
            </form>

a už to jede :)

CZechBoY
Člen | 3608
+
+1
-

Nebo si dej formulář do další samostatný komponenty.
Trošku jsem se v tom zamotal i já.
Pokud už vykresluješ komponentu pro konkrétní produkt a v ní vytvoříš formulář tak už taky víš o jaký produkt se jedná žejo.

Je teda potřeba změnit strukturu komponenty a vytváření taky

class ProductControl extends Control
{
	private $product;

	public function __construct($product)
	{
		$this->product = $product;
	}

    public function render()
    {

        $template = $this->template;
        $template->setFile(__DIR__ . '/Product.latte');

        // vložíme do šablony udaje o produktu
        $template->item = $this->product; // product je v $this->product a už se nepředává přes render parametr

        // a vykreslíme ji
        $template->render();
    }

	protected function createComponentShopForm()
    {
       $form = new Form;

       $form->addHidden('count', 1); // je tohle potřeba?
       $form->addHidden('itemId', 15222); // tohle můžu vynechat, ID produktu mám v $this->product->getId() nebo něco podobného

       $form->addSubmit('send', 'Přidat do košíku');

       return $form;
    }
}

interface IProductControlFactory
{
    /** @return ProductControl */
    function create($product); // tady je potřeba přidat parametr $product, který chci po Presenteru aby mi ho předal
}

Potom v BasePresenteru

	protected function createComponentProduct()
{
		return new Multiplier(function($productID) {
	    $control = $this->productFactory->create($this->products[$productID]); // počítá s tím, že všechny produkty máš v asociativním poli $this->products.
	        return $control;
		});
}

a nakonec v @layout.latte (nebo kde ty produkty vypisuješ)

{foreach $products as $product}

	{control product-$product->getId()} {* nezapomeň na přední IDečka a pomlčku pro Multiplier *}

{/foreach}

Editoval CZechBoY (24. 1. 2017 21:46)

andros
Člen | 145
+
0
-

@CZechBoY Když už si myslím, že jsem komponenty aspoň trošku pochopil, tak mi předvedeš, že vlastně nechápu vůbec nic :)

Co nechápu:
píšeš: Nebo si dej formulář do další samostatný komponenty.
Další kód ale popisuje pořád jednu komponentu. Pořád v jedné komponentě Product je jak vykreslení produktu, tak vykreslení formuláře. V čem je tedy kód, který uvádíš lepší, než původní varianta ?

V BasePresenteru mám metodu createComponentProduct() …, ale BasePresenter produkty ještě nezná. Ty načítám až v konkrétních presenterech, které mají nastarost vypsání produktů.

Kde pak vložím obsah košíku do DB ? Pořád v metodě createComponentProduct() ?

 ...
	$form->onSuccess[] = function ($form, $values) use ($productID) { // use($productID) je potřeba kvůli 			anonymní funkci
 $this->model->addProductToCart($productID, 1 /* nebo $values->count */);
};
return $form;

Děkujii moc za všechny rady, moc mi pomáhají poznávat Nette.

Martk
Člen | 661
+
0
-

Chápu správně, že se komponenta ProductControl se stará o vykreslení právě jednoho produktu? Nic víc? Takhle jsem to pochopil z kódu a komentářů.

Jestli ano, tak nepotřebuješ používat multiplier ve formuláři ShopForm a můžeš to upravit takto:

class ProductControl extends Control
{

    public function render($product)
    {
		$this->product = $product; // Bylo by lepší předávat produkt v konstruktoru, ale vím o struktuře příliš málo
        $template = $this->template;
        $template->setFile(__DIR__ . '/Product.latte');

        // vložíme do šablony udaje o produktu
        $template->item = $product;

        // a vykreslíme ji
        $template->render();
    }

   protected function createComponentShopForm()
   {
           $form = new Form;
           $form->addSubmit('send', 'Přidat do košíku');

			$form->onSuccess[] = function () {
				$idProduct = $this->product['id'];
				$count = 1;
			};

           return $form;
   }
}
interface IProductControlFactory
{
    /** @return ProductControl */
    function create();
}

Kód od @CZechBoY je lepší, ale potřebujeme znát, jak načítáš produkty v presenteru.

CZechBoY
Člen | 3608
+
0
-

Ok trošku spomalím.

Pokud potřebuješ ruční vykreslování formuláře tak máš dvě možnosti

  1. v nadřazené komponentě (nebo presenteru) vykreslit
<form n:name="nazevKomponenty">
...
</form>

a v createComponent vytvořit normálně Form

  1. nebo vytvořit obalovací komponentu (dědící od Nette\Application\UI\Control) na formulář (Nette\Application\UI\Form), komponenta bude mít vlastní šablonu a v ní bude jen ten kod pro formulář – stejně jako v minulém případě
<form n:name="nazevKomponenty">
...
</form>

Výhodu to má takovou, že v presenteru/nadřazení komponentě uvedeš v šabloně pouze

{control formularovaObalujiciKomponenta}

Takže když vykresluješ formulář na více místech tak se vždy vykreslí stejně.

Ten onSuccess je dobrý navázat tam, kde už víš že to tak budeš používat. Když teda víš, že přidávání 1 produktu do košíku budeš pomocí totohle formu dělat v ProductControl komponentě, tak bych navázal onSuccess událost v createComponentShopForm.

Snad jsem tě v tom nezamotal ještě víc… :-)

andros
Člen | 145
+
0
-

@Martk je to takhle:

Komponenta ProductControl vykresluje jeden konkrétní produkt + přidává formulář s tlačítkem „přidat do košíku“.

Bez multiplieru jsem to zkoušel na začátku, ale všechny formuláře na stránce (na jedné stránce je vypsáno více produktů) měli stejné ID a obsahovali stejný produkt (první).

Produkty načítám v presenteru, který dědí od Base presenteru

V presenteru mám:

$this->template->products = $this->model->getProducts()

V modelu:

public function getProducts()
    {
        return $this->connection->table('product');
    }

v šabloně presenteru pak vypisuji produkty takto:

{foreach $products as $product}
     {control product $product}
{/foreach}

tím pro každý vypisovaný produkt na jedné stránce zavolám komponentu Product, který vykreslí jeden konkrétní produkt vč. formu pro vložení konkrétního produktu do košíku.

Editoval andros (24. 1. 2017 23:23)

CZechBoY
Člen | 3608
+
0
-

@Martk Tenhle render workaround nebude fungovat pro formulář.
Odešle se formulář a zpracuje se jako signál, což je o několik kroků dřív v životním cyklu aplikace než renderování šablony, kde se ten product konečně uloží do $this->product.

andros
Člen | 145
+
0
-

Jak popisuje @Martk jsem zkoušel hned na začátku, problém byl, že form měl stejné ID a pro všechny produkty na stránce obsahoval stejné údaje (prvního produktu), proto jsem pátral dál a přišel na multiplier …

CZechBoY napsal(a):

@Martk Tenhle render workaround nebude fungovat pro formulář.
Odešle se formulář a zpracuje se jako signál, což je o několik kroků dřív v životním cyklu aplikace než renderování šablony, kde se ten product konečně uloží do $this->product.

andros
Člen | 145
+
0
-

Tohle docela chápu. bud mít jednu komponentu na vypsání produktu včetně formuláře pro vložení do košíku, nebo mít jednu komponentu pro vykreslení produktu a druhou komponentu pro vykreslení formuláře. V komponentě která vykresluje produkt pak zavolám komponentu vykreslující formulář.
Výhoda je, že formulář můžu vykreslit i jinde, než je jeden produkt (např. v detailu produktu).

CZechBoY napsal(a):

Ok trošku spomalím.

Pokud potřebuješ ruční vykreslování formuláře tak máš dvě možnosti

  1. v nadřazené komponentě (nebo presenteru) vykreslit
<form n:name="nazevKomponenty">
...
</form>

a v createComponent vytvořit normálně Form

  1. nebo vytvořit obalovací komponentu (dědící od Nette\Application\UI\Control) na formulář (Nette\Application\UI\Form), komponenta bude mít vlastní šablonu a v ní bude jen ten kod pro formulář – stejně jako v minulém případě
<form n:name="nazevKomponenty">
...
</form>

Výhodu to má takovou, že v presenteru/nadřazení komponentě uvedeš v šabloně pouze

{control formularovaObalujiciKomponenta}

Takže když vykresluješ formulář na více místech tak se vždy vykreslí stejně.

Ten onSuccess je dobrý navázat tam, kde už víš že to tak budeš používat. Když teda víš, že přidávání 1 produktu do košíku budeš pomocí totohle formu dělat v ProductControl komponentě, tak bych navázal onSuccess událost v createComponentShopForm.

Snad jsem tě v tom nezamotal ještě víc… :-)

Martk
Člen | 661
+
0
-

@CZechBoY Máš pravdu neuvědomil jsem si to.

@andros Zapomněl jsem ještě dopsat, že jsi musel použít multiplier u produktů, protože ty se množí, ne formuláře.

protected function createComponentProduct()
    {
		return new Multiplier(function () {
        	return $this->productFactory->create();
		});
    }

a šablona:

{control product-$product['id'] $product}
CZechBoY
Člen | 3608
+
0
-

@andros To záleží jak používáš tu komponentu no :-).
Pokud to je nějakej mini detail tak asi ve velkým detailu produktu bude ta komponenta k ničemu.

Ještě s něčím máš problém?

andros
Člen | 145
+
0
-

Jak jsi shrnul v jednom předchozím příspěvku celou komponentu, tak mi z toho nebylo jasné toto:

Base presenter:

protected function createComponentProduct()
  {
    return new Multiplier(function($productID) {
        $control = $this->productFactory->create($this->products[$productID]); // počítá s tím, že všechny produkty máš v asociativním poli $this->products.
        return $control;
    });
  }

Base presenter produkty ještě nezná, ty se načítají až v nějakém dalším presenteru, který dědí od base presenter. Pokud to tedy chápu správně, musel bych tuto metodu uvádět v konkrétním presenteru, kde mám načtené produkty k zobrazení. Jenž pak bych tuto metodu musel uvádět ve všech presenterech, kde se vypisují produkty. Když je metoda v basepresenteru, stačí ji napsat jednou pro celou aplikaci.

CZechBoY
Člen | 3608
+
0
-

Počkej počkej počkej. Ty vytváříš komponentu jinde než kde načítáš produkty? Proč?
btw. produkty si v action metodě ulož do $this->products.

andros
Člen | 145
+
0
-

Díky vašim radám mám celou komponentu takhle:

class ProductGridControl extends Control
{
    private $basketManager;
    private $httpRequest;
    private $product;

    public function __construct(BasketManager $basketManager, Nette\Http\Request $httpRequest)
    {
        parent::__construct();
        $this->httpRequest = $httpRequest;
        $this->basketManager = $basketManager;

    }

    public function render($product)
    {
        $this->product = $product;

        $template = $this->template;
        $template->setFile(__DIR__ . '/ProductGrid.latte');

        // vložíme do šablony nějaké parametry
        $template->item = $this->product;

        // a vykreslíme ji
        $template->render();
    }

    protected function createComponentShopForm()
    {
        return new Multiplier(function ($productId) {
            $form = new Form;
            $form->addHidden('count');
            $form->addHidden('name');
            $form->addHidden('price');
            $form->addSubmit('send', 'Přidat do košíku');

            $form->setDefaults([
                'quantity' => 1,
                'prod_id' => $this->product['id'],
                'name' => $this->product['name'],
                'price' => $this->product['d1']

            ]);

            $form->onSuccess[] = function ($form, $values) use ($productId) { // use($productID) je potřeba kvůli anonymní funkci
               $this->basketManager->insert([
                   'quantity' => 1,
                   'prod_id' => $productId,
                   'guest_id' => $this->httpRequest->getCookie('guest'),
                   'name' => $values->name,
                   'price' => $values->price
               ] );

                $this->presenter->flashMessage('Přidali jste zboží do košíku', 'success');
                $this->redirect('this');
            };

            return $form;
        });
    }



}

interface IProductGridControlFactory
{
    /** @return ProductGridControl */
    function create();
}

Asi to není ideální, ale je to funkční a to díky vašim radám :)

CZechBoY
Člen | 3608
+
0
-

Jo, to je taky varianta.
Jen doplním, že nepotřebuješ ten product ukládat

  1. je ti po renderování už k ničemu
  2. nikde jinde než v renderu ho nepoužíváš

Je lepší vždycky vyžadovat interface (pokud je dostupný), tzn. místo Nette\Http\Request použij Nette\Http\IRequest.
Tady máš trošku více info o tom jak správně vyžadovat závislosti http://nette.matej21.cz/cs/di.

andros
Člen | 145
+
0
-

Ano, všechny komponenty vytvářím v base presenteru. Někde jsem to četl, že je to dobré proto, abych mohl komponentu používat kdekoliv a nemusel jsem ji vytvářet na více místech.

produkty načítám z databáze pomocí modelu v metodě renderDefaut(). Vlastně všechny data které posílám do šablony načítám pomocí modelu v metodách renderDefault. Opět jsem to vyčetl v nějakém návodu – je to takhle v návodu na tvorbu první aplikace.

CZechBoY napsal(a):

Počkej počkej počkej. Ty vytváříš komponentu jinde než kde načítáš produkty? Proč?
btw. produkty si v action metodě ulož do $this->products.

andros
Člen | 145
+
0
-

Co myslíš tím , že nepotřebuji ten product ukládat ?

CZechBoY napsal(a):

Jo, to je taky varianta.
Jen doplním, že nepotřebuješ ten product ukládat

  1. je ti po renderování už k ničemu
  2. nikde jinde než v renderu ho nepoužíváš

Je lepší vždycky vyžadovat interface (pokud je dostupný), tzn. místo Nette\Http\Request použij Nette\Http\IRequest.
Tady máš trošku více info o tom jak správně vyžadovat závislosti http://nette.matej21.cz/cs/di.

CZechBoY
Člen | 3608
+
0
-

No pokud ty data využíváš ještě v komponentách tak je dobrý si ty data uložit do třídní proměnné už v action metodě. Potom v render metodě si je z třídní proměnné uložíš do šablony $this->template.

Co se týče komponent v BasePresenteru: komponenty tam dělej pokud je používáš v @layout.latte (a do něj přímo includovaných šablon), nikde jinde to opravdu nemá smysl a je lepší udělat nějakou traitu.

příklad traity (nezkoušel jsem funkčnost)

trait TProductInfoComponent
{
	/**
	  * @var IProductInfoFactory
	  * @inject
	  */
	public $productInfoFactory;

	protected function createComponentProductInfo()
	{
		return $this->productInfoFactory->create();
	}
}
class AbcPresenter extends BasePresenter
{
	use TProductInfoComponent; // a už mám productInfo komponentu
}

Editoval CZechBoY (24. 1. 2017 23:49)

CZechBoY
Člen | 3608
+
0
-

Produkt ukládat do třídní proměnné. tzn. ukládat na pozdější využití jinými metodami.

andros
Člen | 145
+
0
-

Nemyslel jsi tim, ukládat produkt to this->product ?

Takže by to mělo být takto ?

public function render($product)
    {


        $template = $this->template;
        $template->setFile(__DIR__ . '/ProductGrid.latte');

        // vložíme do šablony nějaké parametry
        $template->item = $product;

        // a vykreslíme ji
        $template->render();
    }

andros napsal(a):

Co myslíš tím , že nepotřebuji ten product ukládat ?

CZechBoY napsal(a):

Jo, to je taky varianta.
Jen doplním, že nepotřebuješ ten product ukládat

  1. je ti po renderování už k ničemu
  2. nikde jinde než v renderu ho nepoužíváš

Je lepší vždycky vyžadovat interface (pokud je dostupný), tzn. místo Nette\Http\Request použij Nette\Http\IRequest.
Tady máš trošku více info o tom jak správně vyžadovat závislosti http://nette.matej21.cz/cs/di.

CZechBoY
Člen | 3608
+
0
-

jj, jen se zbavíš tý nepoužívaný třídní proměnný.

andros
Člen | 145
+
0
-

V @layout.latte mám hlavičku a patičku webu, do něj načítám pomocí {incude content} všechny ostatní šablony webu. Každou šablonu mám mám v bloku {block content} {/block}.

CZechBoY napsal(a):

No pokud ty data využíváš ještě v komponentách tak je dobrý si ty data uložit do třídní proměnné už v action metodě. Potom v render metodě si je z třídní proměnné uložíš do šablony $this->template.

Co se týče komponent v BasePresenteru: komponenty tam dělej pokud je používáš v @layout.latte (a do něj přímo includovaných šablon), nikde jinde to opravdu nemá smysl a je lepší udělat nějakou traitu.

příklad traity (nezkoušel jsem funkčnost)

trait TProductInfoComponent
{
	/**
	  * @var IProductInfoFactory
	  * @inject
	  */
	public $productInfoFactory;

	protected function createComponentProductInfo()
	{
		return $this->productInfoFactory->create();
	}
}
class AbcPresenter extends BasePresenter
{
	use TProductInfoComponent; // a už mám productInfo komponentu
}
andros
Člen | 145
+
0
-

Ta proměnná @this->product tam původně nebyla, dal jsem ji tam, během mých různých pokusů s komponentou. Je pravda, že je zbytečná, protože jiná metoda údaje z této proměnné nepoužívá.

CZechBoY napsal(a):

jj, jen se zbavíš tý nepoužívaný třídní proměnný.

CZechBoY
Člen | 3608
+
0
-

Tak potom nepoužíváš tu komponentu na každé stránce a neměla by se vytvářet v BasePresenteru…
Já mám v BasePresenteru komponenty akorát na CSS a JS. V některých webech třeba ještě menu a přihlašovací form.

andros
Člen | 145
+
0
-

No ale když tu komponentu volám ve dvou různých presenterech ?
ted samozřejmně vyzkouším tu tvoji radu a vytvořit traitu. Protože jsem to ale nikdy nedělal, tak se zeptám (a možná blbě).

trait TProductInfoComponent … bude v nějakém zvláštním souboru ? Mám ji dát do nějakého konkrétního adresáře v app ?

CZechBoY napsal(a):

Tak potom nepoužíváš tu komponentu na každé stránce a neměla by se vytvářet v BasePresenteru…
Já mám v BasePresenteru komponenty akorát na CSS a JS. V některých webech třeba ještě menu a přihlašovací form.

CZechBoY
Člen | 3608
+
0
-

Tu traitu dej někam do app složky, ideálně do vlastního souboru (každá třída by měla být ve svém vlastním souboru). Možná ta traita nepojede, nezkoušel jsem. Taky jsem možná blbě napsal název té tovární třídy tak to si kdyžtak uprav.

Napsat vytváření komponenty ve 2 presenterech ještě není taková hrůza :-).

andros
Člen | 145
+
0
-

To máš naprostou pravdu :) Je možná kratší napsat vytváření komponenty ve 2 presenterech, než jednu traitu :) Asi se až moc držím rady "když nemusíš, nepiš nic 2× " :) Tím si to někdy asi dělám složitější než je třeba

Díky moc za všechny rady, dnes jsem se posunul v Nette zase o kousek dál :) A čím dál tím víc se mi líbí.