Řešení: Circular reference detected for services
- quiced
- Člen | 85
Zdravím,
vím, že ohledně této chyby je zde již několik vláken, ale zatím jsem z nich moc nevyčetl jak vyřešit můj případ. Abych nejdříve uvedl kontext většina funkcí aplikace je uložena v třídách na což mám složku /app/src/. Třídy z této složky přes konstruktor načítám do presenterů to je jedna úroveň. Ale protože občas potřebuji pracovat s funkcí z jiné třídy také si ji přes konstruktor načtu. Do dnešního dne mi vše fungovalo bez problému až dnes se mi objevila chyba:
Circular reference detected for services: application.3, reservation, fio.
Kterou konkrétně způsobuje tento kód:
<?php
namespace Fio;
use Apartment\Apartment;
use Reservation\Reservation;
class Fio
{
private $apartment;
private $reservation;
public function __construct(Apartment $apartment, Reservation $reservation)
{
$this->apartment = $apartment;
$this->reservation = $reservation;
}
}
kdy je problém s tímto presenterem:
<?php
namespace AdminModule;
use Apartment\Apartment;
use Nette\Application\Responses\JsonResponse;
use Nette\Application\UI\Form;
use Nette\Utils\DateTime;
use Reservation\Reservation;
use Tracy\Debugger;
class ApartmentPresenter extends BasePresenter{
private $apartment;
private $reservation;
public function __construct(Apartment $apartment, Reservation $reservation)
{
$this->apartment = $apartment;
$this->reservation = $reservation;
}
Pokud z jedné nebo druhé třídy odstraním v konstrukturu třídu Reservation vše začne fungovat. Co mi ale nejde do hlavy je fakt, že u třídy Apartment je to úplně jedno je jak v Reservation tak i ApartmentPresenter a chybu to nehází. Ty dvě třídy pro mě jsou úplně stejné ale vadí pouze jedna z nich a netuším proč.
Přemýšlel jsem i nad tím jak to celé udělat jinak ale aktuální stav mi přijde ideální. Potřebuji ve třídě Fio použít funkce z Reservation stejně tak jako v presenteru ApartmentPresenter potřebuji použít funkce z třídy Reservation.
Nevím jestli ze zaslaného kódu lze vůbec něco zjistit pokud by bylo třeba můžu zaslat více kódu nebo klidně i celý.
Díky za každou odpověď.
- quiced
- Člen | 85
<?php
namespace Reservation;
use Apartment\Apartment;
use Fio\Fio;
use GoSMS\GoSMSSender;
use Nette\Application\LinkGenerator;
use Nette\Database\Context;
use Nette\Mail\IMailer;
use Nette\Utils\DateTime;
use Nuki\Nuki;
use Setting\Setting;
use Setting\Status;
use Nette\Mail\Message;
class Reservation
{
private $connection;
private $apartment;
private $status;
private $setting;
private $fio;
private $goSMSSender;
private $nuki;
private $linkGenerator;
private $mailer;
public function __construct(Context $db, IMailer $mailer, Apartment $apartment, Status $status, Setting $setting, Fio $fio, GoSMSSender $goSMSSender, Nuki $nuki, LinkGenerator $linkGenerator)
{
$this->connection = $db;
$this->apartment = $apartment;
$this->status = $status;
$this->setting = $setting;
$this->fio = $fio;
$this->gosmssender = $goSMSSender;
$this->nuki = $nuki;
$this->linkGenerator = $linkGenerator;
$this->mailer = $mailer;
}
}
- Ondřej Kubíček
- Člen | 494
spíš bych si udělal lepší návrh, třeba další třídu, která by dostala reservation a fio a v ní se prováděly ty společné úkony
- quiced
- Člen | 85
Asi nejjednodušší bude udělat další třídu. Ale stejně mi to přijde takové divné, že budu mít třídu která bude dělat mezičlánek takže místo toho abych udělal:
$this->fio->createPayment()
budu muset udělat:
$this->neco->createPayment();
class neco
{
private $fio;
public function __construct(Fio $fio)
{
$this->fio = $fio;
}
public function createPayment()
{
$this->fio->createPayment();
}
}
Je to v podstatě jenom opsání kódu.
- David Matějka
- Moderator | 6445
uz dle kontruktoru a poctu zavislosti tridy Reservation to vypada, ze toho dela nejak moc, takze drobny refaktoring by urcite neuskodil :)
muzes mi osvetlit, proc fio potrebuje reservation?
- quiced
- Člen | 85
Ve FIO je kontrola stavu platby, která potřebuje změnit stav rezervace pokud je zaplaceno. Tím, že funkce pro změnu stavu rezervace není jenom změna v DB ale třeba posílá mail a další věci chci to mít na jednom místě.
Reservation má 12 funkcí určitě by se to dalo rozdělit. Nadruhou stranu si říkám, na co mít třídu která bude mít 1,2 funkce? Není to zase jenom další zbytečný kód? Takhle je v podstatě vše co se stará o rezervace na 1 místě.
- duke
- Člen | 650
Neříkám, že je to vždycky nejčistší řešení, ale někdy se problém
s cyklickou závislostí služeb dá obejít použitím tzv. accessorů, které
poskytují předání dané služby až na požádání (líně).
Např. máš služby A a B a obě jsou na sobě navzájem závislé. Tento
cyklus můžeš obejít tak, že u jedné z nich nahradíš závislost na té
druhé závislostí na accessoru. Např. A bude vyžadovat B a B bude
vyžadovat accessor služby A. Rozdíl je pak v tom, že ve službě B budeš
muset na accessoru volat navíc metodu get():
$this->aAccessor->get()->foo();
Nette umí tyto accessory generovat automaticky, takže jediné co potřebuješ je napsat si interface s jedinou metodou get() a zaregistrovat ho do configu podobně jako automaticky generovanou továrnu (jen u továren je místo metody get metoda create).