Generované továrničky v Nette 2.1

před 6 lety

Šaman
Člen | 2308
+
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)

před 6 lety

David Matějka
Moderator | 5933
+
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)

před 6 lety

Šaman
Člen | 2308
+
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í.

před 6 lety

Oli
Člen | 1221
+
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ý

před 5 lety

zimmi
Člen | 95
+
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.

před 5 lety

Vojtěch Dobeš
Člen | 1317
+
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.

před 5 lety

castamir
Člen | 631
+
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)

před 5 lety

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)

před 5 lety

enumag
Člen | 2128
+
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.

před 5 lety

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)

před 5 lety

Tomáš Jablonický
Člen | 122
+
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?

před 5 lety

David Matějka
Moderator | 5933
+
0
-

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