Predani promenne (parametru) metode createComponentForm z metody render
- hotline
- Člen | 41
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
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
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.
- hotline
- Člen | 41
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
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.
- 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. - A v tom konstruktoru si ho taky zpracuje, v tomto případě nastaví do své privátní proměnné pro dalši použití.
- 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)
- Šaman
- Člen | 2668
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í.