Struktura inject a závislosti presenterů

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

Zdravím,

mám otázku ke struktuře. Vlastně hledám nejlepší řešení a snažím se to trochu lépe pochopit, jak by se to mělo používat.

Mám modul Akvizice – funkční, umí úkoly, schůzky, správu klientů apod.

Teď bych rád posílal upozornění den předem pro úkoly, schůzky.

Vytvořím si funkci:
Ve zkratce vyhledá úkoly/schůzky k exportu a odešle, když je již odesláno vrací false.

	//class AkvizicePresenter
	actionCronMail()
	{
		$ukolRepository = $this->UkolRepository->vyhledejUkoly($od,$do); //fake zápis
		$schuzkaRepository = $this->SchuzkaRepository->vyhledejSchuzky($od,$do); //fake zápis

		...
		//dnes exportováno?
		return false;

		...
		//exportovat
		$mailer->sendMail();
		return true;
	}

No a teď k mé otázce. Vytvořím si presenter Cron a budu spouštět adresu www.test.com/cron/ třeba každých 10 minut.

class Cron
{
	public function renderDefault()
	{
		$akvizice = new Akvizice();
		$akvLog = $akvizice->actionCronMail();

		if($akvLog) $log->zapsat('AKV - export');
	}
}

Tohle skončí chybou samozřejmě. Protože nemám injectované ukolRepository a schuzkaRepository. Samozřejmě je můžu vložit do inject() v třídě Cron. Ale pokud budu chtít mít víc funkcí projeté funkcí renderDefault nabobtná mi inject v Cron na 20 řádků. A ještě je musím posílat následovně v parametru do funkce actionCronMail.

	//$this = Cron
	$akvLog = $akvizice->actionCronMail($this->ukolRepository,$this->schuzkaRepository);

Tedy chci mít nějakou funkci na posílání. A volat si metody jiných presenterů, kde je připravený výstup. Tak mi to přijde logické, že se dotážu modulu na maily a on mi je pošle a logování a volání řeším někde jinde (v Cron class).

Jak to vyřešit s DI a v Nette?

Díky

Azathoth
Člen | 495
+
0
-

akvizice je v jazyce DI služba, má tedy své závislosti. Například repositáře.
Do akvizice di všechny závislosti injectuj kongruktorem a akvizici si injectuj do class Cron.
A doporučuji si výstup do emailů přesunout do modwlu, pokud k nim chceš přistupovat z více prezenterů. Potom nebudeš muset volatmetody jiných presenterů, ale injectuješ si modelovou třídu zodpovědnou za posílání emailů.

Pavel Kravčík
Člen | 1183
+
0
-

Můžeš trochu rozvést Do akvizice di všechny závislosti injectuj kongruktorem a akvizici si injectuj do class Cron?

Zkoušel jsem přepsat presenter do services a vypadá to v pořádku. Problém je, že se nezavolá inject v té třídě Akvizice. Protože při volání té funkce je pak každé $this jako non-object v metodě actionCronMail. Tudíž předpokládám, že se nevytvořily závislosti v inject() v AkvizicePresenter. Zkoušel jsem tedy volat inject v konstruktoru/startupu. Ale stejný problém.

No můj nápad je takový, že vše co souvisí s tím modulem je v tom modulu a ta metoda vrací jen false/true a vše si obstarává modul.

Azathoth
Člen | 495
+
0
-

promiň ty překlepy, psal jsem z mobilu
injekce konstruktorem
ukázka injekce konstruktorem v nette sandboxu

místo

	/** @var Nette\Database\Context */
	private $database;
	public function __construct(Nette\Database\Context $database)
	{
		$this->database = $database;
	}

tam budeš mít

	/** @var NamespaceTvychRepositaru\UkolRepository */
	private $ukolRepository;

	/** @var NamespaceTvychRepositaru\SchuzkaRepository */
	private $schuzkaRepository;

	public function __construct(NamespaceTvychRepositaru\UkolRepository $ukolRepository, NamespaceTvychRepositaru\SchuzkaRepository $schuzkaRepository)
	{
		$this->ukolRepository = $ukolRepository;
		$this->schuzkaRepository = $schuzkaRepository;
	}

a Akvizici zaregistruj do config.neon jako službu injectni si ji do presenteru nějak takhle

/** @inject @var \NamespaceAkvizice\Akvizice */
    public $akvizice;
Azathoth
Člen | 495
+
0
-

registroval jsi třídu Akvizice v config.neon?
A co znamená, když Akvizici říkáš modul? V nette jsem ten pojem neslyšel, tak nevím, co si pod tím představit, ale možná je to jen tím, že neznám ten pojem (v nette se moduly používají na strukturování presenterů), já bych spíš řekl, že Akvizice je služba.

Pavel Kravčík
Člen | 1183
+
0
-

Ano to jsem zkoušel, ale $ukolRepository v konstruktoru bude přeci prázdný a zahlásí chybu must be an instance of \Namespace\UkolRepository. Protože tohle se řeší v inject() správně. A konstruktoru by to jít nemělo – jestli dobře chápu.

Mám jí jako services registrovanou. Tam problém není. Asi jsem se nepřesně vyjádřil. Hodím struktrukturu. Modul je pro mě složka, kde jsou všechny repository, presentery a templates s spojené s danou jednotkou. Prostě v akvizicích se dělá vše okolo akvizic (správa,výpis,šablony apod.). Tento modul snadno umí připravit HTML výstup z DB těch emailů. Ale posílání bych rád spouštěl něčím jiným, kde nad tím mám kontrolu + log. Od toho byl jiný modul.

Teď mi napadlo to obejít třeba uložením do $session, $db. Ale to neřeší můj dotaz. Jak volat presenter, který by byl naplněný svými závislostmi.

Stejně možná jako kdybych měl Akvizice a chtěl je rozšířit jiným presenterem např. AkviziceDokumenty – ten by dědil od Akvizice (extends) a měl by jiný inject. První má ukolRep,schuzkaRep a druhý by přidal dokumentyRep. V tom případě hodí Nette chybu, že inject není kompatibilní s předkem.

app
	- Akvizice (modul)
		- presenters
			-AkvizicePresenter (tam je metoda actionCronMail)
		- repository
			- ukolRepository
			- schuzkaRepository
	- Cron (modul)
		- presenters
			- CronPresenter
		- repository
			- logRepository

Editoval kzk_cz (4. 12. 2014 15:44)

Pavel Kravčík
Člen | 1183
+
0
-

Aha už to vidím. Při injectu do konstruktoru se musí definovat název služby jako klíč pole. Vyzkouším.

Azathoth
Člen | 495
+
0
-

á, jo takhle jsi to myslel s tou stukturou…já to špatně pochopil…

Doporučuji v presenterech nemít business logiku. Vytvoř si modelovou třídu, kam naházíš business logiku z AkvizicePresenteru a tu modelovou třídu si injectni do AkvizicePresenteru a CronPresenteru.
A když budeš mít akvizici jako modelovou třídu, tak tam injectuj konstruktorem a v CronPresenteru normálně zavoláš metodu té modelové třídy a nebudeš muset řešit, jak z jednoho presenteru volat metody jiného.

Pavel Kravčík
Člen | 1183
+
0
-

On je problém u mě. Já přišel k relativně velkému projektu a neznal jsem Nette a předpokládám, že tím pádem jsem si osvojil spoustu ošklivých zlozvyků, návyků a mylných představ o Nette. Protože ten projekt určitě není příkladem dokonale navrženého Nette projektu.

Jestli dobře chápu pojmy dojmy (google moc nepomohl kromě pár zajímavých flame). Modelová třída je tedy co? A jak se liší od presenteru?

Přes ten konstruktor to ne a ne nastavit. :/ Stále je to none given. A není to náhodou až od Nette 2.1? Tady je 2.0.5.

Samozřejmě to můžu vyřešit tak, že si ty repository budu předávat ručně z Cron presenteru do metody actionCronMail, ale to mi přijde jako strašná prasárna i na mě. :)

A díky za trpělivost.

Azathoth
Člen | 495
+
0
-

Určitě znáš MVC/MVP strukturu.
M-model- business logika aplikace, sem patří repositáře a další věci, které řídí logiku aplikace a „něco dělají“.
V-view, template, šablona.
C/P-presentery- krmí šablonu daty, která získají z modelu a říkají modelu, co má udělat podle aktivit uživatele. Presenter nemá obsahovat business logiku, tu má zařizovat model/služby, které presenter dostane jako závislosti.

Modelová třída je součást modelu z MVC a to je to samé jako service/služba, pokud používám pojmy DI.
Takže si vytvoříš novou třídu (službu), která ti bude zajišťovat business logiku akvizice. Registruješ ji do config.neon jako tam máš repositáře.
Hádám, že akvizice bude mít výše zmíněné repositáře jako závislosti, tedy injectuješ je tam konstruktorem.
A akvizici si injektuješ do akvizicePresenteru přes konstruktor, inject metodu, inject anotaci, tady je to víceméně jedno.
A akvizici si injectuješ i do CronPresenteru.

Hm, s Nette 2.0.5 nemám zkušenosti. Jak tam funguje dependency injection? Jak dostaneš repositáře do presenteru? A jak dostaneš připojení k databázi do repozitářů?

Předávání repositářů ručně je prasárna, od toho máme dependency injection, abychom tohle nemuseli dělat.

Při používání dependency injection by sis zásadně neměl sám vytvářet nové instance presenterů a služeb (tedy používání klíčového slova new), to všechno udělá DI za tebe.

David Kudera
Člen | 455
+
0
-

pokud si to nepletu s jinou verzí, tak v nějaké 2 (chvíli po tom, co byla verze 1, která se přejmenovala na 2..) byla ta trikovačka s metodou setContext v presenterech, která fungovala jako aktuální inject metody. Pracovalo se s tím ale tak pitomě (kvůli dědičnosti), že myslím, že stejně každý sahal na proměnnou context a bral to z ní.

Je to už ale docela hodně dlouho, tak si to možná už nepamatuju nejlíp..

Edit: jinak v ostatních službách tuším constructor inject fungoval normálně..... snad.. asi

Editoval David Kudera (4. 12. 2014 21:19)

Pavel Kravčík
Člen | 1183
+
0
-

Aha jasně. Fakt díky moc za ten čas, který s tímhle strávíte!

V současné chvíli to funguje, že presenter si zavolá inject(7 repositářů tady) a pak s nimi pracuje. Potřebuju vlastníka,klienta úkolu → tak například takto.

Udělám nějaký magic příklad, jak přibližně s tím pracuju (jak jsem se to naučil).

AkvizicePresenter
inject(ukolyRepository $ukolyRepository, ...)
{
	$this->ukolyRep = $ukolyRepository;
	... //další repository 6x
}

function renderMojeUkoly($idAkvizitora)
{
	$akvizitor = $this->akvizitorRepository->vyhledejDleId($idAkvizitora); //return some selection of row
	$klienti = $this->klientRepository->vyhledejMoje($akvizitor->id);
	$schuzky ... //podobné fnce z repo jako výše
	$ukoly ... //podobné fnce z repo jako výše

	/* příprava dat většinou do pole + nějaký ref na jiné tabulky apod. */
	$data = array('nějaká připravená data');

	$this->template->ukoly = $data['ukoly'];
}

A jestli tomu dobře rozumím – správně bych si měl přidat model Akvizice, který mi vyřeší všechno a vrátí $data a v inject presenteru bude jen akviziceModel $akvModel a ten používám jako službu.

Protože teď mají repositáře jen hloupé metody na svoje tabulky (vyhledejDleId(), vyhledejAktivni(), smazatDleId()). I ref jsem vytahoval až do presenteru. Všechno špatně. :(

Ještě musím zkusit inject konstruktorem do těch modelů.

Editoval kzk_cz (5. 12. 2014 7:55)

Pavel Kravčík
Člen | 1183
+
0
-

FUCKING AWESOME

Tohle mi strašně moc pomohlo, konečně jsem to pochopil a můžu to snadno používat. Borci fakt. Když budete mít cestu přes Plzeň… nějaká zlatavá odměna je samozřejmostí.