@inject v service nefunguje v potomkovi

Polki
Člen | 553
+
0
-

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)

Polki
Člen | 553
+
0
-

Update:

Zkusil jsem místo @inject použít metodu inject*. Výsledek je takový, že se metoda inject* vůbec nezavolá. Pokud ale udělám chybu v názvu metody, tak to tracy odchytí jako chybu.

Stále nevím, co s tím.

Polki
Člen | 553
+
0
-

Pomohla tato úprava:

config.neon

services:
    Service1:
        class: FOO\Service1
        inject: true
    FOO:
        class: FOO\BAR
        inject: true

Vůbec se mi ale nelíbí.

Někdo nápady jak to vylepšit?

Mysteria
Člen | 797
+
+5
-

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)

MajklNajt
Člen | 502
+
+3
-

ahoj, v provom rade, injecty sa vykonajú až po __costruct(), takže preto ti dump vráti null, v druhom – inject je určený pre presentery, prečo si tú závislosť jednoducho nepredáš cez konstruktor?

Polki
Člen | 553
+
0
-

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)

Polki
Člen | 553
+
0
-

@Mysteria Díky moc decorator pomohl

Martk
Člen | 661
+
0
-

@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

Editoval Martk (4. 1. 2019 11:20)

MajklNajt
Člen | 502
+
0
-

@Polki

  1. 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
  2. veľký počet závislostí značí zlý návrh aplikácie, skús to rozsekať do menších celkov
  3. 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
+
0
-

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
+
0
-

MajklNajt napsal(a):

@Polki

  1. 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
  2. veľký počet závislostí značí zlý návrh aplikácie, skús to rozsekať do menších celkov
  3. 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…
  1. Chápu, příště uvedu lepší příklad.
  2. 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.
  3. co tím myslíš? Nějaký příklad?
MajklNajt
Člen | 502
+
0
-

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
+
0
-

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.

GEpic
Člen | 566
+
0
-

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.

Editoval GEpic (4. 1. 2019 13:09)

Polki
Člen | 553
+
0
-

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
+
+1
-

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
+
0
-

Š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
+
0
-

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);
?>