Inject repozitáře do vlastní služby

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

Zdravím,
mám problém s injectem repozitáře (Také služba samozřejmě). Chtěl jsem to udělat hlavně přes anotaci @inject, ale když to nešlo, tak jsem zkusil funkci inject*() a nakonec samozřejmě __constructor().

config.neon:

services:
	# Main services
	- App\Model\UserManager
	- App\Forms\SignFormFactory
	- App\ObjectService
	router: App\RouterFactory::createRouter

	# Repositories
	- App\Model\StudentsRepository

BasePresenter.php:

namespace App\Presenters;

use Nette,
	App\Model;

abstract class BasePresenter extends Nette\Application\UI\Presenter{

    /** @var \App\ObjectService @inject */
    public $ObjectService;
}

ObjectService.php

namespace App;

use App,
        App\SchoolObjects;

class ObjectService {

    /** @var \App\Model\StudentsRepository @inject */
    public $StudentsRepository;

    public function CreateObject($class){
        return new $class($this);
    }

}

Jde o to že se mi neprovede inject do vlastnosti ObjectService::$StudentsRepository třída \App\Model\StudentsRepository, ale když zkopíruju tuto vlastnost a dam ji do BasePresenteru, tak tam se injectne. Nevíte někdo jak toho docílit?

Unlink
Člen | 298
+
+2
-

No to je preto, lebo @inject funguje len v presenteroch (dá sa síce zmeniť ale neodporúča sa to) v ostatných prípadoch je nutné použiť konštruktor. Ps. (nieje to __constructor ale __construct).

class ObjectService {

    /** @var \App\Model\StudentsRepository */
    public $StudentsRepository;

    public function __construct(\App\Model\StudentsRepository $studentsRepository){
        $this->StudentsRepository = $studentsRepository;

    }

}

Editoval Unlink (5. 8. 2015 21:58)

holantomas
Člen | 55
+
0
-

Ohledně jména konstruktoru, to byl jen překlep.

Tak se mi nakonec povedlo přes něj sprovoznit. Není nějak lepší řešení? Protože těch repozitářu budu mět více jak 20 a to mi nepřijde už od pohledu jako skvělé řešení.

Jinak děkuji za odpověď

-- EDIT: Teď mě napadlo, nebylo by lepší předat kontext a z něj si repozitáře vytáhnout?

Editoval holantomas (5. 8. 2015 22:22)

Oli
Člen | 1215
+
0
-

To by lepší nebylo. Věř mi, vím o čem mluvím :-)

Když už to děláš jako třídu, tak nikdy nevíš, kde ji budeš ještě potřebovat a pokud by jsi tam dal context, tak pak bude peklo to přenést do jiného projektu a zjišťovat kdeže ti to padá, jaká tabulka chybí atd. Ty závislosti prostě budou schovaný.

Nejlepší řešení IMHO je přes constructor dát věci bez kterých ta třída nemůže fungovat (ideálně jako interface). Protože když to přeneseš do jiného projektu, tak stejně tam ty třídy musí být. To co není bezpodmínečně nutný pro funkci třídy předávat setrem.


Edit: ještě si můžeš vypomoct nějakou třídou, která bude agregovat některé repositáře. Např. jeden agregátor může obsahovat articleRepo, commentRepo, userRepo, … Ale to se mě taky neosvědčilo.

Editoval Oli (5. 8. 2015 22:51)

holantomas
Člen | 55
+
0
-

Jde o to, že služba ObjectService bude obsahovat skoro dvacet repositářů. A předávát všechny přes konstruktor mi nepříjde pěkné. Tak sem chtěl vědět zda by to nešlo „čistěji“

Šaman
Člen | 2640
+
0
-

Čistěji určitě ne. Konstruktor je pro povinné závislosti nejčistější možnost. Ta anotace u presenterů je naopak velmi špinavý způsob, ale presentery mají několik specifických vlastností kvůli kterým se to toleruje (např. konstruktor předka vs. konstruktor potomka). Pokud bys to ale chtěl čistě, tak bys u abstraktních presenterů použil anotaci (nebo metodu inject*) a u konkrétních presenterů taky konstruktor.

Jestli máš dvacet závislostí, tak to vypadá spíš na třídu, která dělá příliš mnoho věcí a tedy porušuje princip jediné odpovědnosti. Pokud ne a třída je nějaká velmi speciální, pak by bylo možné předat celý kontext, ale musíš vědět, že je to hack a proč to děláš. Rozhodně to není čisté řešení.

holantomas
Člen | 55
+
0
-

Jde o to, že ObjectService je v podstatě taková továrníčka na objekty fasády, jestli to mohu tak nazvat. A každy objekt fasady ma i svůj repozitář. Nechtěl jsem tvořit továrnu pro každý objekt. Jednak kvůli pohdlí a navíc jsem se právě chtěl vyhnout obrovskému injectovaní služeb továren do BasePresenteru. Takhle do něj injectu jen ObjectService přes kterou vytvářím dané objekty. Chápu to, že přes konstruktor je to asi nejčistější řešení, ale nepřijde mi elegantní mít v konstruktoru 20 parametrů(možná i více), lepší mi přijde vložit pouze context (Tato část nikdy neopustí tento projekt, takže by neměl vznikat problém s neznámimy závislostmi). Na druhou stranu naprosto chápu co se mi snažíte říct, ale ani jedno řešení zde řečeno mi prostě nepřijde elegantní a zároveň čisté. Předání contextu přes konstruktor mi přijde asi jako nejlepší kompromis. Samozřejmě se mohu plést a pokud by někdo vymyslel lepší řešení jsem mu nakloněn.

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

A constructor je lepší řešení, než toto? Osobně se mi tohle líbí víc. Můžu si to dělit do bloků po pěti a podobně:

//config
services:
    service2:
        class: App\Service2
        setup:
            - setAnotherService

//model
class Service2
{
    private $anotherService;

    public function setAnotherService(AnotherService $service)
    {
        $this->anotherService = $service;
    }
}
Oli
Člen | 1215
+
0
-

No context bude v tvém případě pravděpodobně 2. nejlepší řešení. Možná nejlepší, neznám tvůj projekt. I když mám co se týče objektů relativně velký projekt, tak jsem se nikdy nedostal nad cca 6 – 8 objektů v base presenteru. Tím myslím @inject objektů. Hodně věcí řeším komponentama, který si většinou řeší i vlastní vykreslení. Tím schováš velkou část toho co chceš injectovat pod tu komponentu. Může to potom vypadat třeba takhle.

protected function createComponentShoppingCart($name, \Component\IShoppingCartFactory $shoppingCart)
{
	return $shoppingCart->create();
}

Něco takovýho by ti nepomohlo?

Oli
Člen | 1215
+
0
-

V tomhle případě je to asi nastejno. Nevýhodu vidím v tom, že ti tímhle strašně nabobtná kod. Přidáním další závislosti bys měl o 1 řádek víc v constructoru, ale o 4 řádky víc u tvého řešení. A to ještě nemluvím o tom, že budou mezi metodama prázdný rádky. Takže dejme tomu 5× víc kodu (místa) pro předání závislostí.

Settery vnímám zejména jako nepovinné závislosti. Nemusí to být pravidlem, ale snažím se dodržovat to, že na čem je třída existenčně závislá, tak je v konstruktoru. Pak to je hned zřejmé, když přijdu ke kodu po roce, tak vím, co je nutné a co je nepovinné

Editoval Oli (8. 8. 2015 15:04)

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

Ano, z toho důvodu se mi tohle líbí víc. Že můžu strukturovat jednotlivé injecty. Místo deseti v řadě si je můžu do dát do injectCore, injectSmlouvaRepositories a podobně.

Spíš mi zajímalo, jestli je to dobře nebo špatně. Dovnitř Nette tolik nevidím a tohle se mi líbí víc, než constructor.

holantomas
Člen | 55
+
0
-

Principiálně to výjde na stejno. A v podstatě se snažím o to o čem mluví Oli. Nechci mít nabobtnalí kód. A osobně nevidím nic lepšího na tom že budu mít třeba 4 settery po pěti repozitářích nebo jeden konstruktor o dvaceti repozitářích. I tak děkuji za snahu :)

holantomas
Člen | 55
+
0
-

No o tom jsem mluvil v předchozím komentu. Dalo by se to vyřešit tím že by každý objekt měl továrničku (Služba nebo komponenta principálně to ale vyjde na stejno). Jenže to si myslím že by také nebylo úplně pohodlné. Mým cílem je je něco takového.

// Nejaka action v nejakem presenteru - tohle je hodne zjednodusena myslenka
$pepa = $this->ObjectService->CreateObject('App\SchoolObjects\Student');
$pepa->load($idPepa);
$frantisek = $this->ObjectService->CreateObject('App\SchoolObjects\Student');
$frantisek->load($idFrantisek);
$frantisek->repository->skoc();
$frantisek->save()

Zaroven jde o to abych nemel vsechny repositare definovane v tom konstruktoru (Byt by bylo pekne aby to melo pevne dane zavislosti), ale radsi se dam smere pekneho kodu, kdyz vim ze tahle cast do jineho projektu nepoputuje.

Filip Procházka
Moderator | 4668
+
0
-

Nevyjde to nastejno. Je špatně že máš desítky závislostí v třídách a je špatně že je předáváš přes settery, když jsou povinné. https://filip-prochazka.com/…cky-posobota

holantomas
Člen | 55
+
0
-

Fajn přednáška. V podstatě co bylo řečeno jsem pochopil, pročítáním dokumentace a tady fóra.
O co šlo zde jsem vyřešil. Pak už jsem jen řešil to co je nejlepší kombinace čístěho a elegantního/pěkného řešení. Když to vyreším čistě, bude to otřesně výpadat a přehlednost strašná.

Děkuji moc za odpověď a přednášku :)

holantomas
Člen | 55
+
0
-

Chápu co tím chceš říct. Mě jde jednodušše o to vyhnout se 20-ti parametrům v konstruktoru a pod tím dalším 20-ti zapsáním do property

Oli
Člen | 1215
+
0
-

Pravděpodobně pak ale nemáš úplně dobře návrh logiky :-) Nikdy jsem to nezkoumal, ale řekl bych, že mám průměr tak 5 závislostí na třídu.

Filip Procházka
Moderator | 4668
+
0
-

Jenže ty ses tomu nevyhl, jen jsi použil jinou a horší santaxi.

Filip Procházka
Moderator | 4668
+
0
-

A proč nemůžeš tu třídu rozdělit na více tříd? Každá pak bude mít méně závislostí.