Autowiring SendmailMailer – nefunguje dle dokumentace DI

m.brecher
Generous Backer | 729
+
0
-

Ahoj,

V Tracy DIC je uvedena defaultní služba mail.mailer, autowire=yes, Nette\Mail\SendmailMailer

Autowiring třídy služby skončí výjimkou:

use Nette\Mail\SendmailMailer;

final class TestPresenter extends Presenter
{
    public function __construct(private SendmailMailer $mailer)
    {}
}
Nette\DI\ServiceCreationException
... Service of type Nette\Mail\SendmailMailer required by $mailer in TestPresenter::__construct() not found....

Zpráva výjimky je v rozporu s Tracy DIC, kde je službu typu Nette\Mail\SendmailMailer uvedena.

Autowiring implementovaného rozhraní Nette\Mail\Mailer funguje – vrací službu Nette\Mail\SendmailMailer

use Nette\Mail\Mailer;

final class TestPresenter extends Presenter
{
    public function __construct(private Mailer $mailer)
    {}
}

V dokumentaci je uveden způsob získání služby SendmailMailer:

https://doc.nette.org/cs/mail#…

„Framework automaticky přidává do DI kontejneru službu typu Nette\Mail\Mailer sestavenou na základě konfigurace, a ke které se dostanete tak, že si ji necháte předat pomocí dependency injection.“

Předat pomocí dependency injection znamená uvést v konstruktoru typ třídy služby, nebo typ třídy předka, nebo typ implementovaného rozhraní.

Pro autowiring Nette\Mail\SendmailMailer ale nelze použít typ třídy.

Pokud by to z nějakých důvodů nešlo DI pro mailer dokompletovat, měla by se do dokumentace Maileru doplnit informace, že Nette\Mail\SendmailMailer a Nette\Mail\SmtpMailer nelze autowirovat přímo, ale jedině pomocí rozhraní Nette\Mail\Mailer + srozumitelnější text výjimky: "službu Nette\Mail\SmtpMailer lze autowirovat jenom pomocí rozhraní Nette\Mail\Mailer.

V dokumentaci je sice napsané „přidává do DI kontejneru službu typu Nette\Mail\Mailer“, v Tracy DIC je ale u služby mail.mailer uveden typ Nette\Mail\SendmailMailer.

nightfish
Člen | 471
+
0
-

@mbrecher Koukám se do vygenerovaného kontejneru a v něm je uvedeno

public function createServiceMail__mailer(): Nette\Mail\Mailer
{
	return new Nette\Mail\SendmailMailer;
}

…z čehož plyne, že kontejner zajišťuje vrácení služby typu Nette\Mail\Mailer a nikoliv SendmailMailer. I když se ve skutečnosti vrátí instance SendmailMailer, tak se na to nelze spolehnout.

Pokud je potřeba z DIC získat službu konkrétního typu, tak je vhodné si ji do něj explicitně zaregistrovat (třeba s autowired: self).

Z mého pohledu je tedy dokumentace v pořádku – chování, které popisuješ (V dokumentaci je sice napsané „přidává do DI kontejneru službu typu Nette\Mail\Mailer“, v Tracy DIC je ale u služby mail.mailer uveden typ Nette\Mail\SendmailMailer.) nepozoruji.

Když si do konfigurace doplním:

mail.mailer:
    type: Nette\Mail\SendmailMailer

tak začne kontejner generovat následující kód:

public function createServiceMail__mailer(): Nette\Mail\SendmailMailer
{
	return new Nette\Mail\SendmailMailer;
}

a autowiring začne fungovat dle tvých očekávání.

Pro úplnost ještě přikládám verze balíčků, na kterých jsem to zkoušel:

  • nette/application v3.1.11
  • nette/di v3.1.2
Marek Bartoš
Nette Blogger | 1161
+
0
-

Nette\DI\ServiceCreationException
… Service of type Nette\Mail\SendmailMailer required by $mailer in TestPresenter::__construct() not found…

Tohle by se asi dalo vylepšit informováním, že je třída autowired jen přes Nette\Mail\Mailer

V Tracy DIC je uvedena defaultní služba mail.mailer, autowire=yes, Nette\Mail\SendmailMailer

Stejný případ, místo autowire=yes vypsat autowire=Nette\Mail\Mailer

Jinak je chování záměrné. Je zvykem omezovat autowiring pouze na interface, aby bylo možné implementaci měnit bez zásahu do kódu.

m.brecher
Generous Backer | 729
+
0
-

@MarekBartoš

Jinak je chování záměrné. Je zvykem omezovat autowiring pouze na interface, aby bylo možné implementaci měnit bez zásahu do kódu.

S tím souhlasím, lepší je autowirovat interface, ale bylo by dobré doplnit informaci o omezení autowiringu do dokumentace k Maileru. To bych udělal já, pošlu PR.

…místo autowire=yes vypsat autowire=Nette\Mail\Mailer

Je to jedna možnost, ale lepší by bylo při autowiringu final class místo interface vyhodit výjimku s návodem na opravu. To nevím, jestli bych zvládnul napsat, Ty bys to Marku uměl??

Omezení autowiringu – je omezení na interface standardem i u ostatních defaultních služeb DI Containeru ?? Pokud by jich bylo víc, potom by stálo za to v Tracy DIC ve sloupci autowired uvádět rozsah autowirování, třeba full/interface/none. Plus vedle doplnění do dokumentace Maileru doplnit krátký odstavec o omezeném autowiringu konkrétních služeb do dokumentace k autowiringu.

Marek Bartoš
Nette Blogger | 1161
+
+1
-

Je to jedna možnost, ale lepší by bylo při autowiringu final class místo interface vyhodit výjimku s návodem na opravu.

Ne každá třída je final, ne každá má interface, autowirovatelných interfaces může být implementovaných víc. A jsou situace, kdy může být vyžádání konkrétní implementace záměrné (přetěžování usera)

Omezení autowiringu – je omezení na interface standardem i u ostatních defaultních služeb DI Containeru ?? Pokud by jich bylo víc, potom by stálo za to v Tracy DIC ve sloupci autowired uvádět rozsah autowirování

To je přesně to, co jsem navrhoval s autowire=Nette\Mail\Mailer. Autowire může mít nastaveno true, false, self nebo jednu či více tříd

m.brecher
Generous Backer | 729
+
0
-

@MarekBartoš

To je přesně to, co jsem navrhoval s autowire=Nette\Mail\Mailer. Autowire může mít nastaveno true, false, self nebo jednu či více tříd

OK, znáš DIC perfektně, tahle změna v Tracy DIC by byla užitečná. Jak postupovat, aby se to změnilo? Buďto podat srozumitelné issue, nebo rovnou PR, ale já se ani nevyznám v tom, jak se která defaultní služba může autowirovat, takže bych nenapsal ani srozumitelné isssue. Ty bys do uměl podat?

Já podám PR do dokumentace Maileru.

m.brecher
Generous Backer | 729
+
0
-

@nightfish

Dík za vysvětlení a užitečný tip pro modifikaci služby:

services:
	mail.mailer:
		type: Nette\Mail\SendmailMailer

Stejně funguje i:

services:
	mail.mailer: Nette\Mail\SendmailMailer

Klíč type u modifikace služby jsem v dokumentaci nenašel:

https://doc.nette.org/…ion/services#…

Je to v některé jiné části dokumentace, nebo by bylo vhodné to do dokumentace doplnit ??

m.brecher
Generous Backer | 729
+
0
-

@nightfish

Tak použití klíče type jsem v dokumentaci našel v sekci vytvoření služby:

https://doc.nette.org/…ion/services#…

„Předpokládá se, statická metoda My\Database::create() má definovanou návratovou hodnotu, kterou DI kontejner potřebuje znát. Pokud ji nemá, zapíšeme typ do konfigurace:“

services:
	database:
		create: My\Database::create(root, secret)
		type: PDO

Klíč type v Tvojí ukázce kódu upřesní návratový typ konstruktoru služby na požadovanou hodnotu. Tak teď už jsem to kompletně pochopil. Díky

Marek Bartoš
Nette Blogger | 1161
+
0
-

No, uvidím zda to vůbec rozumně půjde. Informace o typu autowirované služby se afaik do runtime nepřenáší, musela by se pro panel připravit už při kompilaci containeru.

Klíč type u modifikace služby jsem v dokumentaci nenašel:

Protože by se takhle modifikovat neměla. Type je součástí definice služby https://doc.nette.org/…ion/services
Je to tam uvedené jen pro případ, kdy expression v create nedefinuje return type a je potřeba ho doplnit.
Mělo by tam být uvedené i to, že se tím dá return type i změnit a místo konkrétní implementace tak k vytvořené službě přistupovat tak, jako by šlo pouze o její interface.
V sekci o autowiringu chybí to, že type ovlivňuje i nastavení autowired, pokud autowired není explicitně uvedené https://doc.nette.org/…n/autowiring

Jestli k tomu sepíšeš aspoň issue, tak prosím zmiň i to, že create se dřív jmenovalo factory a dokud to funguje a nevznikají žádné deprecation warningy, tak by bylo fajn to mít zdokumentované. Nejspíš se to týká i jiných klíčů, matně si pamatuju že se měnily.

m.brecher
Generous Backer | 729
+
0
-

@MarekBartoš

No, uvidím zda to vůbec rozumně půjde. Informace o typu autowirované služby se afaik do runtime nepřenáší

Co takhle to v Tracy řešit jednoduše pomocí asociativní pole, kde se vyjmenuje těch cca 5 – 6 atypických autowiringů.

Do dokumentace bych doplnil výčet služeb Nette, které nelze autowirovat třídou, ale jen rozhraním. Napočítal jsem jich v Tracy DIC 6, z toho jedna deprecated:

autowiring pouze rozhraním:

  Nette\Caching\Storages\FileStorage  -> rozhraní Nette\Caching\Storage
  Nette\Mail\SendmailMailer, Nette\Mail\SmtpMailer  -> rozhraní Nette\Mail\Mailer
  Nette\Bridges\ApplicationLatte\LatteFactory@anonymous  -> rozhraní Nette\Bridges\ApplicationLatte\LatteFactory
  Nette\Bridges\SecurityHttp\SessionStorage  -> rozhraní Nette\Security\UserStorage
  Tracy\Logger  -> rozhraní Tracy\ILogger
	deprecated:
      Nette\Http\UserStorage  -> rozhraní Nette\Security\IUserStorage

autowiring typem třídy (neimplementují rozhraní)

  Nette\Database\Explorer
  Nette\Database\Connection
  Nette\Application\Request
  Nette\Security\Passwords
  Nette\Security\User
  Nette\Application\LinkGenerator
  Nette\DI\Container
  Nette\Http\RequestFactory
  Nette\Http\Session
  Tracy\Bar
  Tracy\BlueScreen

autowiring obojím – typem třídy i rozhraním:

  Nette\Http\Request i Nette\Http\IRequest,
  Nette\Bridges\ApplicationLatte\TemplateFactory i Nette\Application\UI\TemplateFactory
  Nette\Database\Structure i Nette\Database\IStructure
  Nette\Http\Response i Nette\Http\IResponse

Editoval m.brecher (30. 5. 2023 3:28)

Marek Bartoš
Nette Blogger | 1161
+
0
-

Používají to i balíky mimo Nette a i v Nette si můžeš autowiring přenastavit. Nedává smysl to dělat, když to nebude odrážet realitu.

m.brecher
Generous Backer | 729
+
0
-

@MarekBartoš

Používají to i balíky mimo Nette a i v Nette si můžeš autowiring přenastavit. Nedává smysl to dělat, když to nebude odrážet realitu.

Nemá smysl doplnit informaci o autowiringu do dokumentace? Jestli jsem to pochopil, tak tak jak jsem to poslal to platí jen v defaultním Nette nastavení a v reálné aplikaci to může fungovat jinak?

To je dobrá připomínka, ale většina lidí odhaduji používá autowiring tak, jak je v Nette defaultně nastaven. Pokud si někdo překonfiguruje autowiring, je to pokročilý uživatel, který už takovéto upozornění nepotřebuje. To upozornění je dobré hlavně pro nováčky. A v podstatě hlavně u Maileru – nějaké emaily posílá každý nováček.

Pak bych to do dokumentace formulovat tak, že v DEFAULTNÍM Nette nastavení autowiringu je u těch vypsaných služeb NUTNÉ volat je rozhraním, nikoliv třídou.

Editoval m.brecher (30. 5. 2023 13:05)

Marek Bartoš
Nette Blogger | 1161
+
0
-

Myslím ten výpis v Tracy.

m.brecher
Generous Backer | 729
+
0
-

Marek Bartoš napsal(a):

Myslím ten výpis v Tracy.

Aha, no dobře, že jsem se zeptal. Výpis v Tracy nechám na Tobě, já podám PR do dokumentace. Ten přehled pěti služeb, které nelze autowirovat jménem třídy (deprecated bych tam už nedával) s tím souhlasíš ?? Mrkni na to prosím, ať v tom PR není něco od počátku špatně. Dík