Předávání tříd – chyba Circular reference detected for services

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Andre
Člen | 24
+
0
-

Mám problém s vzájemným předáváním závislostí mezi prezentery a třídami v modelu. Vzhledem k tomu, že třídy se používají v různých metodách po celé aplikaci, nerad bych vytvářel přes new. Nevím, ale jak vyřešit chybovou hlášku Circular reference detected for services, kterou dostávám kvůli tomu, že se třídy vzájemně odkazují na sebe. Fórum jsem procházel, vláken je hodně, ale nikde jsem odpověď nenašel. Pokud místo konstruktorů injectu přes metodu inject*, výsledná chyba je stejná.

Presenter vypadá takto:

abstract class BasePresenter extends Nette\Application\UI\Presenter {

	/** @persistent */
	public $backlink;

	/** @inject @var Nette\Database\Context */
	public $db;

	/** @inject @var \App\Model\OrderManager */
	public $OrderManager;

	/** @inject @var \App\Model\ServiceManager */
	public $ServiceManager;

	...

ServiceManager:

class ServiceManager extends Nette\Object {

    /** @var Nette\Database\Context */
    private $db;

    /** @var \App\Model\OrderManager */
    public $OrderManager;

    /** @var Nette\Application\UI\ITemplateFactory */
    private $templateFactory;

    /** @var Nette\Bridges\ApplicationLatte\ILatteFactory */
    private $latteFactory;

    public function __construct(Nette\Database\Context $db, \App\Model\OrderManager $OrderManager, Nette\Application\UI\ITemplateFactory $templateFactory, Nette\Bridges\ApplicationLatte\ILatteFactory $latteFactory) {
        $this->db = $db;
        $this->OrderManager = $OrderManager;
        $this->templateFactory = $templateFactory;
       	$this->latteFactory = $latteFactory;
    }
...
}

OrderManager:

class OrderManager extends Nette\Object {

	/** @var Nette\Database\Context */
	private $db;

    /** @var \App\Model\ServiceManager */
	public $ServiceManager;

	public function __construct(Nette\Database\Context $db, \App\Model\ServiceManager $ServiceManager) {
		$this->db = $db;
                $this->ServiceManager = $ServiceManager;
	}

Editoval Andre (28. 3. 2015 16:31)

Filip Klimeš
Nette Blogger | 156
+
0
-

Tuhle chybu asi nepůjde vyřešit jinak, než předěláním návrhu aplikace.

Polož si otázku – nešlo by to udělat jinak? Proč má jedna třída závislost na druhé, která má zpětně závislost na té první? Je to nutné?

Pokud dodržuješ klasické vrstvení aplikace, upřímně si nedokážu představit reálný use-case pro Tvůj případ. Zkus ho sem napsat, možná vymyslíme lepší řešení.

Editoval Filip Klimeš (28. 3. 2015 23:41)

piler
Člen | 111
+
0
-

V mojom pripade som natrafil na pripad pri Event dispatcher. Do neho som si zaregistroval subscibers a jeden subscriber dostane ako dependency zase Event dispatcher aby v istej situacii mohol dispatchnut event dalej.

Tu nastava probem, ze Dispatcher ma Subscriber1 a Subscriber1 potrebuje Dispatcher aby mohol dispatchnut event.

Mozno to nie je spravne riesenie, ale nevidim v tom zatial nejaky problem.

Azathoth
Člen | 495
+
-1
-

a co přesně musí dispatchnout event? Používáš na Eventy Kdyby/Events? Pokud ano, tak to se stará o dispatch eventů samo.

piler
Člen | 111
+
0
-

Povedzme je tam command ktory stiahne recent events a prechadza ich po jednom a dispatchuje na zaklade resource type a action nieco ako

<?php
$dispatcher->dispatch(
	sprintf('gc_event_%s_%s', $resourceEvent->resource_type(), $resourceEvent->action()),
	$resourceEvent
);
?>

Povedzme, ze dispatchujem event gc_event_payments_failed na ktory pocuva FailedSubscriber. Tam nastane dalsi check. Ak je to z dovodu nedostatku penazi, tak sa dispatchne event gc_event_payments_insufficient_funds inak sa dipatchne gc_event_payments_unknown.

Tato logika sa zamozrejme da presunut od vlastneho event dispatcheru a mat tam tie podmienky, ale takto mi to pride cistejsie.

Pouzivame symfony dispatcher, pretoze vyuzivame aj priority, co kdyby nema/nemalo. Ako Kdyby/Events circular reference vyriesi, netusim.

Dakujem.

Dik za pomoc.

David Matějka
Moderator | 6445
+
+1
-

@piler kdyby/events ma priority uz pres dva roky: https://github.com/…28464e558e78

Circular reference je tam vyreseny diky Lazy event manageru. Coz ti doporucuju si implementovat i u symfony event dispatcheru, aby se ti vzdy nemusel inicializovat cely graf zavislosti a navic ti to vyresi i ty circular reference :). Symfony samotne pouziva ContainerAwareEventDispatcher, takze bude stacit to naportovat na nette di

piler
Člen | 111
+
0
-

@DavidMatějka myslim, ze sme dispatcher implementovali pred menej ako 2 rokmi, tak som to musel prehliadnut.

Pozeram na ten ContainerAwareEventDispatcher a tu sa v konstruktore zadava container. Pozeram do nasej implementacie a EventDispatcher vytvaram cez DI. Je teda mozme vytvorit Dispatcher cez DI a zaroven mu dat DIC ako zavislost?

Vdaka za rady ;)

David Matějka
Moderator | 6445
+
+1
-

@piler ano, vyzadej si Nette\DI\Container

piler
Člen | 111
+
0
-

@DavidMatějka Super funguje.

Dakujem za pomoc.