@inject v service nefunguje v potomkovi
- Polki
- Člen | 553
Možná nadpis nevystihuje přesně co potřebuji říct.
Jedná se o toto:
config.neon:
services:
- FOO\Service1
FOO:
class: FOO\BAR
inject: true
BAR.php:
namespace FOO;
use FOO\Service1
class BAR{
use \Nette\SmartObject;
/** @var Service1 @inject*/
public $service1;
}
Test.php
namespace FOO;
class Test extends BAR{
public __construct() {
bdump($this->service1); // Vypíše null
}
}
Nějaké nápady jak vynutit aby se v tom předkovi opravdu předala ta závislost?
PS: service1 obsahuje null i v jiných metodách, než v __construct, takže tím, že by se to třeba naplnilo až po konstruktoru to není.
Editoval Polki (4. 1. 2019 1:23)
- Mysteria
- Člen | 797
Polki napsal(a):
Někdo nápady jak to vylepšit?
Nejsprávnější řešení je používat předávání závislostí přes konstruktor, pak nebudeš muset nastavovat nic a @inject anotaci používat maximálně tak v presenterech (na což byla primárně vytvořena).
Pokud na použití @inject trváš z nějakého důvodu (který si umíš ideálně obhájit i jinak, než že jsem línej zduplikovat v potomkovi tu jednu závislost do konstruktoru), tak bych použil decorator:
decorator:
Some\BaseClass:
inject: true
Případně tady taháček s doporučeným předáváním závislostí.
Editoval Mysteria (4. 1. 2019 9:09)
- Polki
- Člen | 553
MajklNajt napsal(a):
ahoj, v provom rade, injecty sa vykonajú až po __costruct(), takže preto ti dump vráti null
@MajklNajt v úvodu jsem psal toto:
Polki napsal(a):
PS: service1 obsahuje null i v jiných metodách, než v __construct, takže tím, že by se to třeba naplnilo až po konstruktoru to není.
Problém byl v tom, že když se nazapne inject pro potomka, tak se inject neprovede v předkovi.
@Mysteria Jde o to, že se snažím vytvořit rozšíření (jde o službu, co něco umí) a toto má umět to, že je vlastně připravená služba, která se zaregistruje do configu a potom z ní dědíš a tím získáš vlastně přesně ty vlastnosti, co ta služba nabízí, což je její účel. Jenže tato služba má závislosti, které potřebuje načíst a tím pádem je třeba je předat například v konstruktoru. Problém je, že těchto závislostí může být třeba 20.
Tím pádem je celkem problém, když někdo, kdo si toto stáhne bude muset v každém potomkovi co si udělá psát tak rozsáhlý konstruktor, když bude potřebovat svoji další závislost načíst.
Edit: 1
Problém je, že závislosti co potřebuje ta služba mají další závislosti,
takže vytváření si ji jinak než pommocí DI by byla sebevražda.
Editoval Polki (4. 1. 2019 11:11)
- MajklNajt
- Člen | 502
@Polki
- uvádzaš príklad kódu, kde sa snažíš dumpnúť premennú v konstruktore, a moja odpoveď znamenala to, že bez ohľadu na fungujúci/nefungujúci inject to v tom konstruktore bude vždy tá východzia hodnota
- veľký počet závislostí značí zlý návrh aplikácie, skús to rozsekať do menších celkov
- pre ľudí, ktorí to budú chcieť používať by bolo vhodnejšie tú tvoju servisu dekorovať ako dediť, potom ti nenastane problém s dependency hell…
- Polki
- Člen | 553
Martk napsal(a):
@Polki použij decorátor co radil @Mysteria . Vyhni se anotaci inject (v presenterech jsou ok), protože porušuje zapouzdření.
final public function injectDependencies(/* tvoje závislosti */) {}
decorator: FOO\BAR: setup: - injectDependencies
@Martk Prosím jaký je rozdíl mezi:
public function injectService1(Service1 $s1) {}
public function injectService2(Service2 $s2) {}
a:
public function injectDependencies(Service1 $s1, Service2 $s2) {}
Jde o nějaký zásadní rozdíl, nebo je to jen věc sytaxe?
- Polki
- Člen | 553
MajklNajt napsal(a):
@Polki
- uvádzaš príklad kódu, kde sa snažíš dumpnúť premennú v konstruktore, a moja odpoveď znamenala to, že bez ohľadu na fungujúci/nefungujúci inject to v tom konstruktore bude vždy tá východzia hodnota
- veľký počet závislostí značí zlý návrh aplikácie, skús to rozsekať do menších celkov
- pre ľudí, ktorí to budú chcieť používať by bolo vhodnejšie tú tvoju servisu dekorovať ako dediť, potom ti nenastane problém s dependency hell…
- Chápu, příště uvedu lepší příklad.
- V reálu tam jsou cca 3, ale pořád je to opakující se otravný kód a kdo ví, jestli tam někdy nějaké nepřibydou.
- co tím myslíš? Nějaký příklad?
- MajklNajt
- Člen | 502
myslím takto – namiesto:
class MojaServisa extends PolkihoServisa {
public function mojaMetoda() {...}
...
}
spraviť:
class MojaServisa {
public $polkihoServisa;
public function __construct(PolkihoServisa $polkihoServisa)
{
$this->polkihoServisa = $polkihoServisa;
}
public function mojaMetoda() {...}
...
}
rozdiel bude potom len v používaní, že konečný užívateľ bude
namiesto $mojaServisa->polkihoMetoda();
volať $mojaServisa->polkihoServisa->polkihoMetoda();
Editoval MajklNajt (4. 1. 2019 12:08)
- Polki
- Člen | 553
MajklNajt napsal(a):
myslím takto – namiesto:
class MojaServisa extends PolkihoServisa { public function mojaMetoda() {...} ... }
spraviť:
class MojaServisa { public $polkihoServisa; public function __construct(PolkihoServisa $polkihoServisa) { $this->polkihoServisa = $polkihoServisa; } public function mojaMetoda() {...} ... }
rozdiel bude potom len v používaní, že konečný užívateľ bude namiesto
$mojaServisa->polkihoMetoda();
volať$mojaServisa->polkihoServisa->polkihoMetoda();
Toto řešení je fajn. Měl jsem to tak původně. Ale pak mě právě napadlo, že udělat to jako to, co píšu ušetří spousty kódů zápisu a taky že jo. Zděděné třídy se smrskly na polovinu a výhoda je, že když napíšeš prostě $this->property, tak se nemusíš starat o to, mít někde závislost a přistupovat k službě $this->service->property, ale prostě je ta property už v té třídě díky dědění. taky nemusíš psát všude use Nette\SmartObject; atd.. a když potřebuješ v potomkovi z té property dostávat hodnotu trochu jinak, tak jen vlastně overridneš metodu get. Hodně důvodů bylo pro to udělat to takto. Ale jak říkám původně jsem to měl jako ty.
- Polki
- Člen | 553
GEpic napsal(a):
Tam jde hlavně o to, aby ty property nebyly public, což s pomocí @inject anotace je problém. Proto se používá pro předávání závislostí ideálně konstruktor a závislosti se přiřazují do private / protected properties.
Jo, ale při inject metodě public ty property nejsou.
- Šaman
- Člen | 2667
Jo, inject metoda je běžný setter (s tím že Nette framework s nimi navíc umí nějak pracovat podle konvence). Takže zapouzdření je ok. Nicméně setterem by se měly předávat jen nepovinné parametry. Vše, co třída nutně potřebuje, to by se mělo předat v konstruktoru (lépe se pak ladí).
- Polki
- Člen | 553
Šaman napsal(a):
Jo, inject metoda je běžný setter (s tím že Nette framework s nimi navíc umí nějak pracovat podle konvence). Takže zapouzdření je ok. Nicméně setterem by se měly předávat jen nepovinné parametry. Vše, co třída nutně potřebuje, to by se mělo předat v konstruktoru (lépe se pak ladí).
Pravda. Otázka ale byla, jaký je rozdíl mezi inject metodami pro jednotlivé property a metodou injectDependencies, která je natáhne naráz.
Editoval Polki (6. 1. 2019 0:55)
- Šaman
- Člen | 2667
Pokud jdou povinné závislosti přes konstruktor, tak žádná
injectDependencies()
nedává smysl. Nepovinné závislosti si
nastavíš kdy a jak potřebuješ kdykoliv v průběhu životního cyklu.
Kromě toho to snižuje čitelnost při ručním vytváření objektu, nebo při
nějaké složitější konfiguraci (když potřebuješ nějakou zavislost zadat
v configu explicitně, třeba když máš víc instancí jedné třídy a tedy
pracuješ se jménem, nikoliv typem).
Dokud je pro tebe automatické injectování závislostí magická skříňka, tak v tom asi rozdíl není. Ale principiálně je v tom stejný rozdíl jako:
<?php
$rect = new Rectangle(3, 5); # dejmež tomu, že pro funkčnost objektu jsou potřeba jen rozměry
$rect->setColor('#0088ff');
$rect->setTransparency(0.25);
$rect->setZIndex(5);
# versus
$rect = new Rectangle;
$rect->getPerimeter(); # error - nemáme ani nutné parametry, náš objekt je v tuhle chvíli neživotaschopný
$rect->setAll(3, 5, '#0088ff', 0.25, 5);
?>