Circular reference detected for
- NouF
- Člen | 67
Ahoj,
snažím se odchytnout chybějící překlady pomocí monologu. Napíšu si
handler a chci uložit tento záznam do DB (CreateTranslateFacade)
Ale když existuje závislost na EntityManagerDecorator, tak to vždy hodí
(Circular reference detected for: contributte.translation.tracyPanel,
contributte.translation.translator, contributte.translation.loaderDb,
nettrine.orm.entityManagerDecorator, nettrine.dbal.connection,
nettrine.dbal.configuration, nettrine.dbal.logger, nettrine.dbal.logger.config,
contributte.monolog.logger.default,
contributte.monolog.logger.default.handler.1)
Jak se tohle řeší?
Děkuju
<?php declare(strict_types=1);
namespace App\Domain\Setup\Translate;
use App\Model\Database\EntityManagerDecorator;
use Nette\DI\Attributes\Inject;
class CreateTranslateFacade
{
#[Inject]
public EntityManagerDecorator $em;
/**
* @param array<string, scalar|null> $data
*/
public function createTranslate(
string $key,
string $locale,
string $message,
string $domain,
string $path,
): Translate
{
$translate = new Translate($key, $locale, $message, $domain, $path);
$this->em->persist($translate);
$this->em->flush();
return $translate;
}
}
- Lumeriol
- Generous Backer | 63
Circular reference ti říká, že třída A, která má nějakou závislost B, tak ta závislost B (nebo její podtřída C, …) načítá právě onu třídu A. Jednoduše řečeno se ti to zacyklí, protože nemohou být na sobě závislé vzájemně. Je tak třeba najít ty dvě třídy, které jsou na sobě závislé a upravit si to.
- nightfish
- Člen | 518
@NouF Pokud se jedná o cyklus závislostí ve třídách, jejichž obsah/funkčnost nemůžeš ovlivnit (což podle řetězce contributte.translation.tracyPanel, contributte.translation.translator, contributte.translation.loaderDb, nettrine.orm.entityManagerDecorator, nettrine.dbal.connection, nettrine.dbal.configuration, nettrine.dbal.logger, nettrine.dbal.logger.config, contributte.monolog.logger.default, contributte.monolog.logger.default.handler.1 tak vypadá), nezbývá než to vyřešit na úrovni tvého kódu.
Můžeš např. ve třídě CreateTranslateFacade
nahradit
public EntityManagerDecorator $em;
za
public \Nette\DI\Container $container;
a následně zavolat
$em = $this->container->getByType(EntityManagerDecorator::class); $em->persist($translate); $em->flush();
.
Pěkné to není, ale jiné řešení mě v tomto případě nenapadá.
- Marek Bartoš
- Nette Blogger | 1274
Obejít se to dá přes accessor. Jen je třeba ho nevolat během
inicializace služby, jinak se to zase zacyklí.
https://doc.nette.org/…tion/factory#…
Nejspíš to není vina žádné konkrétní služby. Zkrátka sis vytvořil závislosti, které jsou vzájemně provázané tak, že je DI container nedokáže vytvořit. Ideální řešení je služby přestrukturovat tak, aby na sobě závislé takto nebyly, ale v kombinaci překladače, loggeru a připojení k databázi nastává cyklická závislost celkem běžně a není triviální to vyřešit. Tím, že jednu službu načteš později než při inicializaci (constructor, inject, …) problém obejdeš.
Editoval Marek Bartoš (7. 10. 1:34)
- m.brecher
- Generous Backer | 871
Alternativou k accessoru je použít getByType() di containeru
use Nette\DI\Container;
class OneClass
{
public function __construct(private readonly Container $container)
{}
public function getOtherClass(): OtherClass // nevolat dříve než ve startup()
{
return $this->container->getByType(OtherClass::class);
}
}
- mystik
- Člen | 312
Ja to jeste c jednom nasem projektu resil tak ze sem mel lazy vilane inject metody.
Vytvorila se sluzba A_partial tridy A jen konstruktorem s vyplym
autowiringem.
Vytvorila se trida B konstruktorem kam se explicitne pridalo A_partial
A pri vytvareni sluzby A se pouzilo A_partial, zavolaly jeho injecty vcetne
injectB
- Marek Bartoš
- Nette Blogger | 1274
Alternativou k accessoru je použít getByType() di containeru
Tak přesně takhle se DIC používat nemá.
- m.brecher
- Generous Backer | 871
@MarekBartoš
Tak přesně takhle se DIC používat nemá.
Proč? Kvůli znepřehlednění kódu a zamlžení závislostí třídy? V konstruktoru mám deklarované použití $container a pomocí moderního IDE snadno zjistím, jaká služba se z $container-u získá. Službu vrací deklarovaná metoda, službu nevytvářím uprostřed bloku kódu. Oproti accessoru ušetřím psaní souboru interface a zápisu do konfiguračního souboru. Řekl bych, že použití accessoru a getByType() jsou cca rovnocenné varianty.
- Marek Bartoš
- Nette Blogger | 1274
Protože se nedozvíš o neexistující, neautowirované nebo duplicitní službě při kompilaci DIC. Používáš DI jako service locator. A když už máš složitější appku, kde se na třídu odkazuje na hodně místech a může být i více služeb stejného typu, tak oceníš možnost podívat se, kde všude se ta konkrétní služba používá.
Editoval Marek Bartoš (7. 10. 1:26)