BaseControl inject – nefunguje?

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

Chci používat komponentu pro práci s flashMessages napíč všemi komponenty, tak jsem si vytvořil BaseControl.

namespace AppModule\Component;

use Nette\Application\UI\Control;

class BaseControl extends Control
{

	/**
	 * @var JiriNapravnik\Component\FlashMessage\IFlashMessageFactory
	 * @inject
	 */
	public $flashMessageFactory;

	public function __construct(){
		parent::__construct();
	}

	protected function createComponentFlashMessages()
	{
		return $this->flashMessageFactory->create();
	}

}

v neonu mám potom:

services:
	-
		class: AppModule\Component\BaseControl
		inject: true

Pokud však zadám v šabloně
{control flashMessages}

Dostanu Call to a member function create() on a non-object. Prostě $this->flashMessageFactory je nullový…

Přitom když se podívám do vygenerovaného kontejneru, tak je zdá se vše v pořádku:

	/**
	 * @return AppModule\Component\BaseControl
	 */
	public function createService__122()
	{
		$service = new AppModule\Component\BaseControl;
		$service->flashMessageFactory = $this->getService('97_JiriNapravnik_Component_FlashMessage_IFlashMessageFactory');
		return $service;
	}

pokud v tom createComponentFlashMessages udělám před return na zkoušku, tohle, jestli nevrací null místo služby, tak to funguje dle očekávání…

$this->flashMessageFactory = $this->getPresenter()->context->getByType('JiriNapravnik\Component\FlashMessage\IFlashMessageFactory');
Oli
Člen | 1215
+
0
-

Zkoušel jsi to přes inject metodu? Nevím jestli to pomůže, jen takovej nápad. Viz. cheatsheet o di.

Jiří Nápravník
Člen | 710
+
0
-

Zkoušel výsledek je stejný. Jen vygenerovaný kód DI kontejneru se liší, že místo:

/**
 * @return AppModule\Component\BaseControl
 */
public function createService__122()
{
    $service = new AppModule\Component\BaseControl;
    $service->flashMessageFactory = $this->getService('97_JiriNapravnik_Component_FlashMessage_IFlashMessageFactory');
    return $service;
}

je

/**
 * @return AppModule\Component\BaseControl
 */
public function createService__122()
{
    $service = new AppModule\Component\BaseControl;
    $service->injectFlashMessageFactory($this->getService('97_JiriNapravnik_Component_FlashMessage_IFlashMessageFactory'));
    return $service;
}

ale nefunguje stejně

David Matějka
Moderator | 6445
+
0
-

jak vytvaris konkretni komponentu (ukaz jak registraci v neonu tak presenter)? proc mas basecontrol registrovany jako sluzbu?

Jiří Nápravník
Člen | 710
+
0
-

BaseControl mam jako sluzbu, z toho duvodu abych tam mohl dat tu FlashMessageFactory. Pokud bych ji musel dat pres konstruktor, tak bych pak musel prespisovat vsechny konstruktory, co od ni dedi a dopadlo by to stejne jako kdybych injectoval v Presenteru.

Tady je pak třeba ta komponenta, co od BaseControlu pak dědí:

final class ForgottenPasswordForm extends BaseControl
{

	private $formFactory;
	private $userFacade;

	public function __construct(FormFactory $formFactory, UserFacade $userFacade)
	{
		parent::__construct();

		$this->formFactory = $formFactory;
		$this->userFacade = $userFacade;
	}

	protected function createComponentForm()
	{
		$form = $this->formFactory->create();
		...
		$form->onSuccess[] = array($this, 'processForm');

		return $form;
	}

	public function processForm(UI\Form $form)
	{
		...
	}

	public function render()
	{
		....
	}
}

interface IForgottenPasswordFormFactory
{

	/**
	 * @return ForgottenPasswordForm
	 */
	public function create();
}

Neon:

services:
	- UserModule\FrontModule\Components\User\ForgottenPassword\IForgottenPasswordFormFactory

šablona:

{control flashMessages}
{control form}
David Matějka
Moderator | 6445
+
0
-

ta registrace BaseControlu je naprosto zbytecna, ta sluzba se nikde nevyuzije, budes vyuzivat jen tu IForgottenPasswordFormFactory ⇒ musis nastavit inject: true u kazde konkretni factory

Jiří Nápravník
Člen | 710
+
0
-

Ježkovi voči, já jsem debil:D Jo máš pravdu, mě to nějak vůbec nedošlo, že to je jen klasický dědění a čumím to na to jak blbec:)

A nenapadá tě nějaké lepší řešení, jak bych tohle mohl udělat jen jednou a nemusel zapínat pro každý Control inject: true? Protože od BaseControlu budu chtít dědit každou komponentu, a nechce se mi moc všude připisovat inject: true. Viděl jsem tu někde i řešení, že se nastavil inject pro vše, ale to zase moc nechci. Prostě jak nejjednodušeji vyřešit, abych měl v každé komponentně ten flashMessage.

David Matějka
Moderator | 6445
+
0
-

mozna by slo pouzit dedicnost sluzeb, ale moc si tim nepomuzes. nebo si vytvorit compiler extension, ktery to zapne

Jiří Nápravník
Člen | 710
+
0
-

Nakonec jsem to vyřešil trošku ošklivě přes sáhnutí na context a vytáhnutím getByType.

A pokud by chtěl někdo jiný, tak ten enabler, je třeba tady.

Díky za pomoc

Filip Procházka
Moderator | 4668
+
0
-

No a nebo použiješ Kdyby/Autowired component factories, které fungují i v komponentách :)

Šaman
Člen | 2668
+
0
-

Přesně z tohohle důvodu jednotného chování všech komponent bych se přimlouval za (možnost) zapnutí inject autowire nad komponentami. Aby mohl zůstat konstruktor pro věci kontkrétní třídy.

Filip Procházka
Moderator | 4668
+
0
-

@Šaman nechceš napsat rozšíření které projde služby a zapne inject pokud je to komponenta? Mohlo by to přijímat list interfaců na které by to ty injecty zapínalo :) Presentery, komponenty, …