Predani promenne (parametru) metode createComponentForm z metody render

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

Dobry vecer,

potrebuji formular v komponente, kde pridam v latte promennou takto:
{control test $var}

Promenna $var mi pak pristane v metode render, to je v poradku, ale potrebuji ji znat i pri vytvareni formulare. Jaky je nejlepsi zpusob pro predani promenne?

Mam i jine komponenty, ktere nepracuji s formulari, ale pouze s metodou handleClick, kde mi tyto parametry predane v latte pristanou v metode handleClick a do metody render jen poprve, pri dalsim pozadavku (ajax) nikoli, takze to resim zpusobem:

private $id;

public function render($id = NULL){
if(is_null($id)) $id = $this->id; //tady $id neni, vezmu si ho
}

public function handleClick($id){
$this->id = $id; //tady $id je, predam
}

Tam je to funkcni v poradku, ale nevim, jestli je to idealni reseni.

Kazdopadne k aktualnimu problemu – takto vypada komponenta s formularem. V metode render je parametr v poradku predan, ale pri vytvareni formulare uz ne. Napada me tedy snad jedine, ze se nejdrive vola vytvoreni formulare a pak render, to vlastne dava smysl, ale pak neni mi jasne, proc to funguje ve vyse uvedenem prikladu. Nebo se pletu a chyba je jinde? Jak resite situaci, kdy potrebujete do formulare vepsat neco, co znate jedine z presenteru?

<?php
	use Nette\Application\UI\Control;


	class Test extends Control
	{

	private $database;
	private $var;

		public function __construct(Nette\Database\Context $database)
		{
			parent::__construct();
			$this->database = $database;
		}

		public function render($var)
		{
			$template = $this->template;
			$template->setFile(__DIR__ . '/Test.latte');
			$this->template->render();

			$this->var = $var; //hodnota $var je tady v poradku
		}

		protected function createComponentForm()
		{
			$var = $this->var;
			Nette\Diagnostics\Debugger::dump($var); //= NULL

			$form = new \Nette\Application\UI\Form();
			//...
			return $form;
		}
	}


	/** rozhraní pro generovanou továrničku */
	interface ITestFactory
	{
		/** @return \Test */
		function create();
	}

Diky za rady, preji pekny zbytek vikendu.

Editoval hotline (14. 6. 2015 14:04)

David Matějka
Moderator | 6445
+
+2
-

Pokud je to mozno, tak je nejlepsi si tuto hodnotu rovnou predat z presenteru. Tedy v createComponent* metode, napriklad pres setter nebo do create metody tovarny. pr.

1. setter

class MyComponent extends Control
{
	private $id;

	public function setId($id)
	{
		$this->id = $id;
	}
}

//presenter

class MyPresenter extends BasePresenter
{

	protected function createComponentMyComponent()
	{
		$control = ....;
		$control->setId($this->id);
		return $control;
	}
}

2. tovarna

class MyComponent extends Control
{
	private $id;

	public function __construct($id, Nette\Database\Context $database)
	{
		$this->id = $id;
		...
	}
}

interface IMyComponentFactory
{
	public function create($id);
}

//presenter

class MyPresenter
{
	protected function createComponentMyComponent()
	{
		return $this->myComponentFactory->create($this->id);
	}
}

je dulezite, aby se parametry v konstruktoru komponenty a v metode create v tovarne jmenovaly stejne. nette uz to pak propoji :)


Pokud neni mozno predat to id z presenteru (napriklad pokud mas nejaky vypis a vykreslujes X formularu jen s rozdilnym id), tak pouzij Multiplier

hotline
Člen | 41
+
0
-

Ahoj, diky moc za ukazky. Vybral jsem si druhy zpusob, tedy pres tovarnicku. Vypada to, ze jsem to udelal stejne jako v ukazce, ale hodnoty jsou vsude v komponente NULL.

Presenter:

<?php

	namespace App\Presenters;

	use Nette,
	App\Model;


	/**
		* Test presenter.
	*/

	class TestPresenter extends BasePresenter
	{
		public $database;
		private $id;

		public function __construct(Nette\Database\Context $database)
		{
			$this->database = $database;
		}

		public function renderDefault($url)
		{
			$this->id = "test";

		}

		/** @var \ITestControl @inject */
		public $testControl;

        protected function createComponentTestControl()
		{
		$control = $this->testControl->create($this->id);
		return $control;
		}

	}

Komponenta (tovarnicka):

<?php
	use Nette\Application\UI\Control;


	class TestControl extends Control
	{

	private $database;
	private $id;

		public function __construct(Nette\Database\Context $database)
		{
			parent::__construct();
			$this->database = $database;
			$this->id = $id;
			Nette\Diagnostics\Debugger::dump($this->id); // = NULL
		}

		public function render()
		{
			$template = $this->template;
			$template->setFile(__DIR__ . '/Test.latte');
			$this->template->render();
		}

		protected function createComponentForm()
		{
			$test = $this->id;
			Nette\Diagnostics\Debugger::dump($test); // = NULL

			$form = new \Nette\Application\UI\Form();
			//...
			return $form;
		}
		public function processForm($form)
		{
		//..
		}
	}

	/** rozhraní pro generovanou továrničku */
	interface ITestControl
	{
		/** @return \TestControl */
		function create($id);
	}

(omlouvam se, nemam v nazvech Factory, ale jde o tovarnu)

Mam nekde chybku nebo jsem nekde na neco zapomnel? Snad jsem spravne pochopil tu cast v presenteru, tam jsem si nadefinoval private $id; a predal hodnotu v metode render. Je to tak v poradku? Diky moc.

Mysteria
Člen | 797
+
+1
-

Minimálně místo

public function renderDefault($url) {
	$this->id = "test";
}

musí být

public function actionDefault($url) {
	$this->id = "test";
}

protože render* se volá až po createComponent*.

hotline
Člen | 41
+
0
-

Aha, diky, uz koukam na zivotni cyklus :) upravil jsem a hodnota se uz predava v poradku.

        protected function createComponentTestControl()
        {
        $control = $this->testControl->create($this->id);
		Nette\Diagnostics\Debugger::dump($this->id); // = spravny udaj
        return $control;
        }

Ale jeste tam bude nejaky problem, v komponente je porad vsude null. Zkusil jsem dat do metod __construct, render a createComponentForm

			Nette\Diagnostics\Debugger::dump($id);
			Nette\Diagnostics\Debugger::dump($this->id);

a vsude bohuzel NULL.

Editoval hotline (14. 6. 2015 15:19)

Šaman
Člen | 2668
+
+2
-

Ach jo. Hádej, kde se v té ukázce nastavuje $this->id. Ne, nemáš to stejně, jako v té ukázce. U tebe tam o nějakém id není ani zmínka, kromě definováni privátní proměnné toho názvu. DI je strašně jednoduchá věc, jenom na to asi pořád chybí třeba dobrý videotutoriál.

  1. To id musíš z továrničky předat do komponenty, resp. ta si o něj musí říct (to je jediné a základní pravidlo DI). Řekne si o něj v konstruktoru.
  2. A v tom konstruktoru si ho taky zpracuje, v tomto případě nastaví do své privátní proměnné pro dalši použití.
  3. Jediná magie je v tom, že vygenerovaná továrnička předá id tomu konstruktoru. (Pokud si o něj ten konstruktor řekne.)

Edit: Myslím, že generované továrničky jsou pro spoustu uživatelů temná a nepochopitelná magie. Přitom nedělají nic jiného, než že vytvoří komponentu a předají jí její závislosti buď z DI kontejneru, nebo z parametru metody create (pokud se shodně jmenují, to je ten tvůj případ).

Editoval Šaman (14. 6. 2015 15:58)

hotline
Člen | 41
+
0
-

Moc se omlouvam, v konstruktoru jsem to trochu prehledl. Diky za vysvetleni, uz mi to zacina byt jasne, priznavam, magii jsem v tom trochu videl. :-)

Šaman
Člen | 2668
+
+3
-

Na pochopení generovaných továrniček je nejlepší si jednu tovární třídu napsat ručně. Bude to obyčejná třída zaregistrovaná jako service, která si ale bude muset (ideálně konstruktorem) injectovat databázi. Pak bude mít metodu create($id) a v ní pomocí operátoru new vytvoří instanci komponenty a předá jí to $id a $database. A takto připravenou komponentu vrátí.

Jenže tohle je úplně tupý kus kódu, který by se ale musel přepisovat pokaždé, pokud by třeba ta komponenta chtěla dalši závislot (třeba nějaký kus modelu). Když použiješ generované továrny (definované tím rozhraním), tak se ta komponenta vytvoří rovnou se všemi závislostmi z DI kontejneru a do toho rozhraní uvedeš jen ty závislosti, které chceš předat pomocí té metody create. Jinak řečeno, pokud bys té komponentě chtěl předat třeba UserRepository, tak to zapíšeš jen uvnitř komponenty a na rozhraní továrničky nemusíš sahat.

Takže třida + tovární třída není Nette. Předávání závislostí pomocí DI není Nette. Nette jen připravilo DI kontejner (který umí vrátit už pripravené služby i s jejich závislostmi) a Nette také umožňuje úplně vynechat psani továrniček, protože je umí připravit samo, jen z definice rozhraní.