Generované továrničky v Nette 2.1

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

Nejdřív malé shrnutí toho jak napsat generovanou továrničku, protože v dokumentaci je stále již nedoporučovaný způsob.

Máme nějakou komponentu:

<?php

/**
 * Komponenta přihlašovacího formuláře.
 */
class LoginForm extends Control
{
…
}

?>

Tuto komponentu nechceme vytvářet v presenteru pomocí operátoru new (z mnoha dobrých důvodů, které ale nejsou tématem tohoto dotazu). Takže si na ni vytvoříme tovární třídu a tu si zaregistrujeme jako službu v configu. Počkat, to je moc psaní. Nette za nás tuto tovární třídu umí vygenerovat samo, když mu řekneme jakou komponentu chceme vytvářet. Potřebujeme jen rozhraní:

<?php

/**
 * Rozhranní pro generovanou továrničku.
 */
interface ILoginFormFactory
{
	/** @return LoginForm */
	function create();
}

?>

V konfigu si necháme vytvořit službu podle tohoto rozhraní

services:
	- ILoginFormFactory

… a pak v presenteru můžeme využívat vygenerovanou službu LoginFormFactory:

<?php

/**
 * Aplikační presenter.
 */
class ApplicationPresenter extends BasePresenter
{
	/**
	 * @var ILoginFormFactory
	 */
	protected $loginFormFactory;


	/**
	 * Injector.
	 */
	public function injectLoginFormFactory(ILoginFormFactory $loginFormFactory)
	{
		$this->loginFormFactory = $loginFormFactory;
	}


	/**
	 * Vytvoří komponentu loginForm.
	 * @return LoginForm
	 */
	protected function createComponentLoginForm()
	{
		return $this->loginFormFactory->create();
	}
}

?>

A teď pár dotazů:
Výše uvedený způsob funguje nad aktuální stabilní verzí (2.1.1). V dokumentaci jsou bohužel stále generované továrničky řešené pomocí sekce factories, která je v 2.1 deprecated. A na fóru jsem našel několik doporučení, jak tyto továrničky psát, ale zápis je delší a nevím, v čem je jeho výhoda.

  1. V definici služby se doporučuje vypnout autowiring. Proč? Továrna je přece úplně obyčejná služba, která má konkrétní rozhranní pomocí kterého ji mohu injectovat všude, kde potřebuji LoginForm.
# proč se doporučuje tento zápis?
services:
	-
		class: ILoginFormFactory
		autowired: no
  1. Pak jsem viděl i tento zápis, ale nějak nevím, v čem je jeho výhoda. V presenteru s tím pracuji stejně, jako v ukázce nad čarou, nebo ne?
services:
    -
        class: LoginForm
        implement: ILoginFormFactory

Děkuji za připomínky.
Teď se peru s předáváním parametru pomocí metody create, až mi to bude v základu fungovat, hodím to sem taky, ať to máme pohromadě v jenom dobře dohledatelném vlákně.

Editoval Šaman (13. 2. 2014 17:44)

David Matějka
Moderator | 6445
+
0
-
  1. to doporuceni autowired: no je pro stare tovarnicky (takove to $context->createSomething(), nyni myslim $context->createService('something')), nevztahuje se na generovane tovarnicky
  2. class je nutno specifikovat pokud nemas uvedenou @return anotaci. bylo to take nutne, pokud jsi uvadel argumenty – v dev uz to nemusis uvadet

Editoval matej21 (13. 2. 2014 18:01)

Šaman
Člen | 2659
+
0
-

Díky. Chápu to tak, že to nad čarou je nejkratší možný best practise na generované továrničky. (Zkrátit by to šlo ještě pomocí property injection do presenteru – anotace @inject).
Vypnout autowired je zbytečné a ten druhý způsob pod čarou se použije pokud potřebuji třídě předat parametry, nebo provést nějaké další nastavení.

Oli
Člen | 1215
+
0
-

Šaman napsal(a):
Teď se peru s předáváním parametru pomocí metody create, až mi to bude v základu fungovat, hodím to sem taky, ať to máme pohromadě v jenom dobře dohledatelném vlákně.

Teď jsem to řešil: tady to je popsaný

zimmi
Člen | 94
+
0
-

@Šaman jak pomocí továrničky předat do komponenty informaci o jejím rodiči? Pokud tak neučiním, Nette mi zakřičí, že komponenta není připojená k presenteru. Díky.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

@zimmi Stačí komponentu z továrničky vrátit – bude k rodiči připojena automaticky. Je třeba se ujistit, že se to tak provádí v celé hierarchii.

castamir
Člen | 629
+
0
-

@Šaman: Ten zápis je trošku kostrbatý, ale tohle by mělo odpovědět na tvé dotazy

services:
	-
		implement: IFormFactory
		setup:
			- setFoo

Editoval castamir (10. 3. 2014 9:18)

vojty
Člen | 19
+
0
-

vojtech.dobes napsal(a):

@zimmi Stačí komponentu z továrničky vrátit – bude k rodiči připojena automaticky. Je třeba se ujistit, že se to tak provádí v celé hierarchii.

Tak nevím co dělám špatně, mám komponentu App\Neco, App\NecoFactory která implementuje App\INeco s metodou create která vrací App\Neco

neon takto:

<?php
	neco: App\NecoFactory
?>

presenter:

<?php
    /**
     * @return \App\Neco
     */
    protected function createComponentNeco()
    {
        return $this->context->neco->create();
    }
?>

to bohuzel vrati err:

Component '' is not attached to ‚Nette\Application\UI\Presenter‘

pokud to trochu poupravim a připojim ho k prezenteru ručně tak to funguje

<?php
    /**
     * @return \App\Neco
     */
    protected function createComponentNeco()
    {
        return $this->context->neco->create($this, 'neco');
    }
?>

ale to se mi zdá fakt nějaký divný:)…zvojtil jsem „neco“? :) Jinak používám v2.2

Editoval vojty (25. 5. 2014 16:49)

enumag
Člen | 2118
+
0
-

Evidentně používáš $this->getPresenter() v konstruktoru té třídy App\Neco. To je ale příliš brzy, komponenta v tu chvíli ještě nemá vazbu na presenter. Běžným řešením je metoda attached.

vojty
Člen | 19
+
0
-

enumag napsal(a):

Evidentně používáš $this->getPresenter() v konstruktoru té třídy App\Neco. To je ale příliš brzy, komponenta v tu chvíli ještě nemá vazbu na presenter. Běžným řešením je metoda attached.

Hmm…díky. Metodu $this->getPresenter() v App\Neco používám, ale ne v konstruktoru…to je právě divný. Mohl bys mi prosím, ještě prosím trochu osvětlit ten postup s attached?

Bdw. dřív jsou to používal takto:

<?php
factories:
    neco:
        parameters: [parent, name]
        arguments: [%parent%, %name%]
        class: App\Neco
?>

třída vypadá zhruba takto:

<?php
class Neco extends Nette\Application\UI\Control
{
	public function udelej($name, $vars)
	{
		...
		$link = $this->getPresenter()->link($name, $arr)
	}

	...
	...
}
?>

Edit:
Tak moje chyba, neuvědomil jsem si, že nemůžu udělat toto:

<?php
public function createComponentNeco()
{
	$neco = $this->context->neco->create()
	$neco->udelej(...)
	return $neco;
}
?>

Jaký je tedy ted best practise pro připojení k prezenteru ihned? Díky.

Editoval vojty (25. 5. 2014 18:07)

Tomáš Jablonický
Člen | 115
+
0
-

Škoda, že se nedá nějakým způsobem ten interface vytvořit v neonu – tedy, že by si to Nette samo vytvořilo. Nebo to jde?

David Matějka
Moderator | 6445
+
0
-

@jablon: nastesti nejde :) nette se pouze stara o implementaci existujiciho interface