vytvoření komponenty v nette 2.1. bez presenteru

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

Ahoj, snažím se aktualizovat na nette 2.1 a po po pár hodinách mam dojem, že to můžu všechno zahodit. Přečetl jsem https://doc.nette.org/…tion/factory, koukal na přednášku https://www.youtube.com/watch?… ale stejně v tom plavu.

Asi chápu jak vytvořit komponentu z presenteru – vytvořim si továrnu, jako službu injectuji do presenteru a v metodě createComponentKomponenta zavolám create nad factory.
Co jsem ale nepochopil, jak definovat komponentu v neonu.

Hodně jsem používal komponenty jen v šablone {control supervisorClient} s tím, že továrnička byla jen v konfigu:

supervisorClient:
	class: web123\Components\SupervisorClient

Presenter tedy o nějaké komponentě vůbec nevěděl, což bylo supr, protože jsem u každého zákazníka mohl upravovat libovolně šablony, konfig ale presenter zůstal beze změny.
Jak toho docílit v Nette 2.1 ?

Napsal jsem si generovanou factory i ručně napsanou, ale stále dostávám chybu, že komponenta neexistuje.

supervisorClient:
	implement: web123\Components\ISupervisorClientFactory
namespace web123\Components;

interface ISupervisorClientFactory
{
    /** @return SupervisorClient */
    function create();
}

Jde to použít i bez presenteru aniž bych definoval někde createControlSupervisorClient ?

Jaký je nejsnazší způsob migrace továrniček z 2.0 do 2.1 ? Musím opravdu napsat desítky interfaců pro factory nebo existuje něco snašího?

Díky.

Šaman
Člen | 2668
+
0
-

Továrničky potřebovaly interface vždycky. Jestli jsi ho nepoužíval, tak jsi tu komponenty definoval jako služby, což lze stále. Jediné, co se změnilo, je sdružení sekcí factories a services do jedné, už jsou pouze services.
Můžeš si napsat do BasePresenteru supertovárnu createComponent($name), která ti vrátí instanci správné komponenty. Může si ji vytáhnou z kontejneru jako službu, může ji v nejhorším i sama vytvořit. Ale rozhodně to není čisté a už vůbec ne best practise!

P.S. Sorry, první odstavec možná popisuje vývojovou verzi před vydáním 2.1. Ale druhý platí, jen je to na úkor čitelnosti a bezpečnosti (každý presenter může vytvořit jakoukoliv komponentu a nemáš nad tím kontrolu).

Filip111
Člen | 244
+
0
-

Teď jsem možná trochu mystifikoval – mě to „bez“ presenteru fungovalo, protože už dávno supertovárničku používám:

protected function createComponent($name) {
	if (method_exists($this->getContext(), $method = 'create' . ucfirst($name))) {
		$component = $this->getContext()->$method($name);
		return $component;
	}
	return parent::createComponent($name);
}

Presenter tedy obejít nelze? Vždy musí existovat metoda createControlXXX a nové Factory třídy nebo iterfacy
jen usnadňují napsání createControlXXX metody v presenteru?

(začínám si myslet, že ne – ale tak jsem si na to zvykl, že jsem to považoval za Nette feature)

Šaman
Člen | 2668
+
0
-

Komponentu musíš vytvořit v kontejneru (ve smyslu komponenta schopná obsahovat jiné komponenty, neplést s aplikačním DI kontejnerem). Kontejnery jsou presentery a všechny komponenty (extends Control).
Každá komponenta vzniká v továrničče obecné createComponent($name), nebo konkrétní createComponentName(). Tak tomu bylo už od 0.9 a je tomu stále.
Factories a interfacy se netýkají primárně komponent, je to nástroj, jak z DI kontejneru dostat buď stále stejnou instanci (services), nebo pokaždé novou instanci třídy (staré factories, dnes services pomocí interface). Ale to, že se dá komponenta vykreslit v šabloně pomocí makra {control name} je jen díky továrničce, která v presenteru, nebo nadřízené komponentě musí (a vždy musela) být.

David Grudl
Nette Core | 8282
+
0
-

Rozdíl mezi 2.0 a 2.1 je v tom, že ty metody nemají prefix create, ale createService. Lepší je použít přímo metodu createService:

    if ($this->getContext()->hasService($name)) {
        $component = $this->getContext()->createService($name);
        return $component;
    }

Ale používání getContext() není best practice. A vidím v tom i bezpečnostní riziko, protože změnou v URL mohu tahat libovolné služby.

Filip111
Člen | 244
+
0
-

Díky, začínám se tím prokousávat – na createService jsem přišel, když jsem si prošel vygenerovaný SystemContainer.
Nikdy jsem neuvažoval o tom, že změnou url můžu vlastně zavolat jinou komponentu nebo teď dokonce i službu. Asi by se dalo vymyslet, jak toho zneužít.

Každopádně jsem přišel na způsob, jak to rozběhnout bez velkých změn – tedy stačí zakomentovat v konfigu sekci factories: a přesunout ji do services + následující supertovárnička:

	protected function createComponent($name) {
		if ($this->getContext()->hasService($name)) {
			$component = $this->getContext()->createService($name);

			if (method_exists($component, 'create'))
				// vytvarim komponentu pomoci Factory nebo IFactory
				return $component->create();
			else
				// vracim komponentu puvodne definovanou v konfigu v sekci factories:
				return clone $component;
		}
		// hledam tovarnicku createComponentName v presenteru
		return parent::createComponent($name);
	}

Tohle „funguje“, bezpochyby je to prasárna.
No jdu psát interfacy pro factory, abych to klonování služeb smazal dřív než si ho všimnete :)

David Grudl
Nette Core | 8282
+
0
-

V takovém případě ale interface nijak nevyužíváš, naopak ti překážení. Takže je pak lepší zůstat u způsobu definice class: xxx místo implement: xxx.

Filip111
Člen | 244
+
0
-

Říkal jsem si, že k některým komponentám dodělám Factory, které pak správně injectované do presenteru použiji v továrničce createComponentNazev.
Je ale pravda, že v okamžiku, kdy definuji službu s Factory se mi komponenta vytvoří pomocí uvedené super továrny, což je blbý – opuštění konceptu supertovárny ve stávajících projektech je skoro nemožný.

David Matějka
Moderator | 6445
+
0
-

@Filip111: tak to udelej obracene, nejdriv zkus createComponent* a az pak pouzij supertovarnu..

protected function createComponent($name) {
	$component = parent::createComponent($name);
	if($component) {
		return $component;
	}
...supertovarna

}
Filip111
Člen | 244
+
0
-

@matej21:
To by šlo a funguje to dobře – zkoušel jsem inject služby továrny do presenteru a klasický createComponentNazev.
Staré projekty a komponenty v nich se tedy vytvářejí přes supertovárnu, nové můžu již dělat klasicky jak se má. Akorát dokud tam bude supertovárna, bude existovat riziko zneužití.

David Matějka
Moderator | 6445
+
0
-

jo, tenhle pristup neni z hlediska bezpecnosti idealni. mohl by sis udelat nejaky whitelist, s komponentami, ktere je mozno vytvorit supertovarnou