Circular reference detected for

NouF
Člen | 67
+
0
-

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

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.

NouF
Člen | 67
+
0
-

Ahoj, dekuju za reakci.

Tušil jsem, o co se jedná čekal jsem spíš, že nekdo napíše zE je to chováni implementace monologu atd.. takze spravny postup identifikovat tridu (asi EntityManager) a zkoušet odmazavat zavislosti? Nebo je na to lepší postup.

nightfish
Člen | 518
+
0
-

@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
+
+3
-

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

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

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

Alternativou k accessoru je použít getByType() di containeru

Tak přesně takhle se DIC používat nemá.

m.brecher
Generous Backer | 871
+
0
-

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

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)

m.brecher
Generous Backer | 871
+
0
-

@MarekBartoš

Protože se nedozvíš o neexistující, neautowirované nebo duplicitní službě při kompilaci DIC.

Přesvědčil Jsi mě, accessor je v tomto směru lepší řešení.