[výzkum] používáte Container::findByType() nebo findByTag()?

David Grudl
Nette Core | 8082
+
0
-

Nettí DI kontejnery sebou nesou poměrně velká metadata, která zajišťují funkčnost Container::findByType() a findByTag().

Rád bych metadata zmenšil, protože se domnívám, že se v praxi tyto metody tolik nepoužívají. Podotýkám, že se bavím o metodách přímo kontejneru, nikoliv ContainerBuilder.

Pokud tyto metody používáte, napište mi prosím k čemu konkrétnímu.

(Příklad: nette/application 2.4 si pomocí tagu nette.presenter vytahovala názvy služeb s presentery.)

LukasV
Člen | 5
+
+1
-

Ne myšlena i metoda getByType() nebo jen ty dvě zmíněné?

repli2dev
Člen | 57
+
0
-

Používám(e) findByType v DI extensionech, v testech a případě že používám třeba jen Nette DI a chci v nějaké místě vytahovat konkrétní závislosti (protože v daném místě to nejde řešit jinak).

dada-amater
Bronze Partner | 52
+
0
-

Různá rozšíření pouzivaji findByTag. Z hlavy mě napadá třeba starší Kdyby/Events. Ten use case mi přijde celkem praktický.

David Grudl
Nette Core | 8082
+
0
-

LukasV napsal(a):

Ne myšlena i metoda getByType() nebo jen ty dvě zmíněné?

Ne, ta je z celého kontejneru nejdůležitější :-)

David Grudl
Nette Core | 8082
+
0
-

dada-amater napsal(a):

Různá rozšíření pouzivaji findByTag.

Bylo by schůdné řešení, kdyby extension nově oznámila, které tagy chce exportovat? Třeba pomocí:

$builder->addExportedTag('xxxx');
David Grudl
Nette Core | 8082
+
0
-

dada-amater napsal(a):

Z hlavy mě napadá třeba starší Kdyby/Events. Ten use case mi přijde celkem praktický.

Když koukám na kód Kdyby/Events, vidím tam volat findByTag() jen na builderu… Nad kontejnerem to vidím jen v Kdyby/RabbitMq.

David Grudl
Nette Core | 8082
+
0
-

repli2dev napsal(a):

Používám(e) findByType v DI extensionech, v testech a případě že používám třeba jen Nette DI a chci v nějaké místě vytahovat konkrétní závislosti (protože v daném místě to nejde řešit jinak).

Mohl bys mi to popsat co nejkonkrétněji prosím?

Milo
Nette Core | 1283
+
+2
-

Občas jsem použil findByType(). Zejména v jedné modulární aplikaci. Jednotlivé moduly měly vlastní config.neon soubor a v něm přidávaly služby do kontejneru. Jádro aplikace nebo nějaké obecné moduly si potom v DI kontejneru hledaly podle interfacu. Například bin/cli-admin hledal App\Cli\IAdminModule anebo daemon, hlídající alarmy, hledal App\Monitoring\IAlarmProvider.

Už ani nevím, proč jsem to v DI kontejneru hledal v runtime. V compile time by to šlo taky. A teď v novém DI bych si to asi nechal injektovat přes type().

2bfree
Člen | 248
+
0
-

Používám pro zjištění, jestli už služba (závislost) byla registrována někým jiným dříve. Třeba FlySystem.

Když mi dáš @DavidGrudl mail a public key, nastavim ti read práva na repo, ať to vidíš.

2bfree
Člen | 248
+
-1
-

Ale v podstatě to nejdůležitější vypadá takto:

<?php
    /**
     * Service alias setup helper
     *
     * @param string $type
     * @param string $alias
     * @return bool
     * @throws ServiceCreationException
     */
    protected function setupServiceAlias(string $type, string $alias): bool
    {
        $builder = $this->getContainerBuilder();
        $services = $builder->findByType($type);
        $serviceName = key($services);
        $isServiceAliasAdded = false;
        if ($serviceName !== null) {
            if (next($services) !== false) {
                throw new ServiceCreationException(sprintf('Multiple services of type "%s" found', $type));
            }
            $builder->addAlias($this->prefix($alias), $serviceName);
            $isServiceAliasAdded = true;
        }
        return $isServiceAliasAdded;
    }
?>
Kacer_Bob
Člen | 7
+
0
-

Metodu findByType() používám k nalezení služby implementující určitý interface, ale nepotřebuji, aby mi DI kontejner vyráběl instance všech těch služeb.

V podstatě bod 2 zde: https://github.com/…/di/pull/178#…

Taky používám findByType() v kombinaci s isCreated(), protože neexistuje ekvivalentní metoda k isCreated(), která by měla jako parametr třídu služby. Proto přes findByType() zjistím název služby (je registrovaná jako anonymní) a pak volám isCreated().

David Grudl
Nette Core | 8082
+
+2
-

Děcka ještě pro jistotu jednou, řeč není o builderu a compiler extensions, ale o tom, co děláte s $container.

Marek Bartoš
Nette Blogger | 1146
+
0
-

V aplikaci to používáme pro lazyloading factories pro loginy skrze sociální sítě. Přidávají se dynamicky a není možné tak injectovat konkrétní factories.

Imho ale vůbec není problém to nahradit, kupříkladu v contributte/event-dispatcher si v LazyEventDispacher předáváme jen názvy služeb a získáváme je přes getService().

U findByTag() nevím – tagy používám jen pro hromadnou konfiguraci služeb, nikdy pro jejich získávání. Přijde mi to jako zastaralé a nyní už absolutně zbytečné řešení, když se dají nahradit skrze interface a názvy služeb v compile-time.

Jediný problém, co mě napadá je pořadí extensions pracujících se stejným type – v run-time už máš služby ze všech extensions, v compile-time jen z extensions, které jsou zaregistrované dřív.

Jinak zmenšení metadat je super nápad. V kontajneru o 10k řádků nám dělají 3k jen metadata, které php nemůže nijak optimalizovat.
I parametry mají nějakých 100 řádků, které jsou tam úplně zbytečně, protože se jich většina používá jen při kompilaci.

Felix
Nette Core | 1183
+
0
-

Projel jsem skoro vsechny kody contributte / nettrine a apitte:

  • findByTag (7×) – volany jenom v CompilerExtension
  • findByType (45×) – 44× volany jenom v CompilerExtension

Jediny, kde je pouzito findByType v runtime je u nettrine/orm u ManagerRegistry.

Coz by slo nejspis predelat. :-)


Realne na projektu to moc nepouzivame, jenom vyjimecne. Kdyz se nadtim zamyslim, tak by slo vsechno napsat pres findByType, ale u findByTag vidim pohodlnost v tom, ze mam napr. 5 typu sluzeb (dost casto nejaky lazy vytvareni komponent) co chci z DI vytahnout. A vsem tem sluzbam dam tag a v kodu si pak rozhodnu co s tim chci delat. Jasne, slo by to zavolat 5× s findByType, ale 1× findByTag je v tomhle pohodlnejsi.

Jinak jak pise @Mabar.

xificurk
Člen | 121
+
0
-

Container::findByType() používáme na několika místech v testech pro:

  1. Inspekci služeb registrovaných ve vygenerovaném kontejneru – kontrolujeme např. že jsou všechny služby určitého typu opravdu zaregistrovány v kontejneru
  2. Nahrazení originální (anonymní) služby nějakým mockem pro konkrétní testcase.

Tagy obecně v runtime nepoužíváme.

Editoval xificurk (5. 3. 2019 20:40)

Honza Kuchař
Člen | 1662
+
0
-

Používám oboje. Přes tagy si značím veřejné služby, které si potom vytahuji a registruji v druhého kontejneru. Šlo by řešit i staticky přes kontejner, pokud bude možné injektovat všechny služby daného typu do array/variadického parametru.

getByType a findByType používám hlavně v testech. Druhé použití pro mě je, když mám dva kontejnery. Vnější registrovaný jako službu v tom vnitřním a potřebuji z vnějšího podle interface vytáhnout služby.


Příklady:

findByTag() – Při více kontejnerech v sobě – Nette DI scope, kde se z vnitřního kontejneru musí po kompilaci registrovat služby v tom vnějším.

findByType() – v commandu, který vytahuje všechny process managery:

	protected function execute(InputInterface $input, OutputInterface $output): int
	{
		foreach ($this->container->findByType(ProcessManagerType::class) as $serviceName) {
			$type = $this->container->getService($serviceName);
			\assert($type instanceof ProcessManagerType);

			$output->writeln("Starting process manager <comment>$serviceName</comment>");

			$this->runProcessManager($type, $output);
			$output->writeln("<info>Process manager</info> <comment>$serviceName</comment> <info>finished - no more events to process</info>.\n");
		}

getByType() – při vytahování služby podle interface z vnějšího kontejneru

services:
	clock:
		class: Brick\DateTime\Clock
		create: @outerContainer::getByType(Brick\DateTime\Clock)

Editoval Honza Kuchař (5. 3. 2019 22:10)