__construct v base presenteru i v ostatních presenterech

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

Ahoj, potřeboval bych na svých stránkách generovat v záhlaví menu. Menu se bude trochu lišit například podle toho, kde se uživatel nachází, nebo zda li je přihlášený, nebo ne…

Můj plán byl takový, že jsem si na to uděla metodu v MenuReposiotry.php a pak v basepresenteru jsem udělal například toto:

$private $menu;

public function __construct(Model\MenuRepository $menu) {
        $this->menu = $menu;
}

public function beforeRender() {
        $this->template->leftItems = $this->menu->generateLeftMenu();
        $this->template->adminItems = $this->menu->generateAdminMenu();
}

To sice fungovalo, ale do té doby, než jsem si například udělal nějaký jiný presenter, který měl taky __construct(). Při pokusu o načtení se mi dostala chyba: Call to a member function generateLeftMenu() on a non-object.

Jak to vyřešit? Zatím mě napadá jen nepoužívat v basepresenteru __construct, ale použít $this->context->…Ale context se prý už nemá používat… Díky…

EDIT – našel jsem zúsob, ale nevím, zda je to dobré řešení → v basePresenteru místo __construct použít inject…

Editoval n.u.r.v. (13. 11. 2013 14:02)

David Matějka
Moderator | 6445
+
0
-

jasne, inject* metody jsou na tohle delany.. pak jeste muzes pouzit @inject anotaci (nevim, v jake je to verzi) nebo @autowired

jinak, pokud je to mozny, tak je casto lepsi si cely menu vyclenit do komponenty

Casper
Člen | 253
+
0
-

Injektovat závislosti můžeš do presenteru v Nette 2.1dev třemi způsoby:

Inject anotací:

class UserPresenter extends SecuredPresenter {
	/** @var \Model\Facade\PaymentFacade @inject */
	public $paymentFacade;
}

Inject metodami:

class UserPresenter extends SecuredPresenter {
	/** @var \Model\Facade\PaymentFacade */
	public $paymentFacade;

	public function inject(\Model\Facade\PaymentFacade $paymentFacade){
		$this->paymentFacade = $paymentFacade;
	}
}

Konstruktorem:

class UserPresenter extends SecuredPresenter {
	/** @var \Model\Facade\PaymentFacade @inject */
	public $paymentFacade;

	public function __construct(\Model\Facade\PaymentFacade $paymentFacade){
		$this->paymentFacade = $paymentFacade;
	}
}

V BasePresenteru se samozřejmě konstruktor hodí nejméně, protože tím nutíš všechny podtřídy definovat stejnou závislost a tu předávat ručně. A proto vznikly obě další možnosti.

MartinitCZ
Člen | 580
+
0
-

V případě Nette presenterů zapomente, že __constructor exituje!!!
Pokud potřebuješ předávat závislosti, tak anotací @inject, funckcí injectName(…) a nebo přes $this->context.

romiix.org
Člen | 343
+
0
-

martinit napsal(a):

V případě Nette presenterů zapomente, že __constructor exituje!!!
Pokud potřebuješ předávat závislosti, tak anotací @inject, funckcí injectName(…) a nebo přes $this->context.

Z akého dôvodu je „zakázané“ (?!) v presentroch používať konštruktor?

Casper
Člen | 253
+
0
-

martinit:

Spíš bych řekl: kontruktor využít jen tehdy, pokud potřebuješ injectovat nějakou cestu apod, které nemůžeš specifikovat typehint. Tam prostě jiná volba není, pokud nechceš používat $this->context. Nicméně pokud potřebuješ v BasePresenteru nějakou cestu, context bych volil.

n.u.r.v.
Člen | 485
+
0
-

martinit napsal(a):

V případě Nette presenterů zapomente, že __constructor exituje!!!
Pokud potřebuješ předávat závislosti, tak anotací @inject, funckcí injectName(…) a nebo přes $this->context.

Na školení bylo výslovně řečeno, že $this->context už nepoužívat ,ale používat __construct…

David Matějka
Moderator | 6445
+
0
-

@martinit: doporucovat $this->context namisto konstruktor injection chce odvahu :))

kratce:

  • $this->context skryva zavislosti
  • konstruktor je nejlepsi zpusob predani zavislosti, jelikoz neumozni vytvorit instanci bez tech zavislosti
  • inject metody a syntactic sugar v podobe @inject anotaci vzniknul jako obrana proti depedency hellu v konstruktoru
llook
Člen | 407
+
0
-

Pokud chceš použít constructor injection, tak se to dělá tak, že konstruktor potomka vyžaduje kromě svých vlastních závislostí i všechny závislosti předka:

class ConcretePresenter extends BasePresenter
{
	private $otherService;

	public function __construct(Model\MenuRepository $menu, SomeOtherService $otherService)
	{
		parent::__construct($menu);
		$this->otherService = $otherService;
	}
}

Když přidáš novou závislost do konstruktoru BasePresenteru, tak musíš aktualizovat konstruktory všech jeho potomků, což může být solidní opruz.

Proto existuje možnost injektovat přes inject metody. Často se závislosti BasePresenteru vkládají přes inject a závislosti potomků přes konstruktor. Samotný Nette\Application\UI\Presenter má také spoustu závislostí, vkládané právě přes inject metodu, aby ses tím v potomcích nemusel zabývat: Nette\Application\UI\Presenter::injectPrimary().

Není to nejčistší řešení, ale je funkční a celkem použitelné.

n.u.r.v.
Člen | 485
+
0
-

matej21 napsal(a):

jasne, inject* metody jsou na tohle delany.. pak jeste muzes pouzit @inject anotaci (nevim, v jake je to verzi) nebo @autowired

jinak, pokud je to mozny, tak je casto lepsi si cely menu vyclenit do komponenty

Ahoj, ještě jsem našel jeden způsob, takže dotaz – který z níže uvedených způsobů je lepší?

1)

abstract class BasePresenter extends Nette\Application\UI\Presenter {
private $menu;

public function injectMenu(Model\MenuRepository $menu) {
        $this->menu = $menu;
    }
...
...
public function beforeRender() {
$this->template->menu = $this->menu->generateMenu();
}

2)

use Model\MenuRepository;

....
....

abstract class BasePresenter extends Nette\Application\UI\Presenter {
public function beforeRender() {

        $menu = new MenuRepository($this->getUser());
        $this->template->menu = $menu->generateMenu();
    }

}
romiix.org
Člen | 343
+
0
-

Záleží od okolitých podmienok. Potrebuješ službu vo viacerích (ideálne všetkých) potomkoch triedy BasePresenter? Ak áno, patrí do ich predka.

Ak by mal byť kód potomka BasePresentru v metóde beforeRender() vždy rovnaký – daj ho do predka, ale nezabudni na začiatku metódy v potomkoch (ak bueš potrebovať metódu rozšíriť) volať parent:beforeRender().

David Matějka
Moderator | 6445
+
0
-

@n.u.r.v.: prvni zpusob je lepsi.. mel by ses co nejvice vyhybat rucnimu instancovani. kdyz budes potrebovat zmenit zavislosti, tak dle druheho zpusobu bys to musel vsude, kde to pouzivas. kdyz to mas jako sluzbu, kterou injectnes, tak se o vse postara di container, pripadne zavislosti doplnis pres neon.