Struktura inject a závislosti presenterů
- Pavel Kravčík
- Člen | 1196
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
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 | 1196
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
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
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 | 1196
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 | 1196
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
á, 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 | 1196
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
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
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 | 1196
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 | 1196
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í.