Autowiring SendmailMailer – nefunguje dle dokumentace DI
- m.brecher
- Generous Backer | 871
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 | 518
@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 | 1274
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 | 871
@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 | 1274
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 | 871
@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 | 871
@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 | 871
@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 | 1274
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 | 871
@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 | 1274
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 | 871
@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)
- m.brecher
- Generous Backer | 871
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