Wytworzone fabryki
Nette DI może automatycznie generować kod fabryczny na podstawie interfejsu, oszczędzając na pisaniu kodu.
Fabryka to klasa, która tworzy i konfiguruje obiekty. W związku z tym przekazuje im również ich zależności. Proszę nie mylić z wzorcem projektowym factory method, który opisuje specyficzny sposób używania fabryk i nie jest związany z tym tematem.
Jak wygląda taka fabryka, pokazaliśmy w rozdziale wprowadzającym:
Nette DI może automatycznie wygenerować kod fabryczny. Wystarczy, że stworzysz interfejs, a Nette DI wygeneruje jego
implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie create
i deklarować typ zwrotny:
Tak więc fabryka ArticleFactory
ma metodę create
, która tworzy obiekty Article
. Klasa
Article
może wyglądać na przykład tak:
Dodaj fabrykę do pliku konfiguracyjnego:
Nette DI wygeneruje odpowiednią implementację fabryczną.
W kodzie, który korzysta z fabryki, żądamy obiektu przez interfejs, a Nette DI używa wygenerowanej implementacji:
Fabryka z parametrami
Metoda fabryczna create
może otrzymywać parametry, które następnie przekazuje do konstruktora. Na przykład
uzupełnijmy klasę Article
o ID autora artykułu:
Dodamy również parametr do fabryki:
Ponieważ parametr w konstruktorze i parametr w fabryce mają tę samą nazwę, Nette DI przekazuje je automatycznie.
Zaawansowana definicja
Definicja może być również zapisana w formie wieloliniowej za pomocą klawisza implement
:
Pisząc w ten dłuższy sposób, można podać dodatkowe argumenty do konstruktora w kluczu arguments
oraz
dodatkową konfigurację za pomocą setup
, tak jak w przypadku zwykłych serwisów.
Przykład: gdyby metoda create()
nie akceptowała parametru $authorId
, moglibyśmy w konfiguracji
określić stałą wartość, która zostałaby przekazana do konstruktora Article
:
Lub odwrotnie, gdyby create()
zaakceptował parametr $authorId
, ale nie był on częścią
konstruktora i został przekazany przez metodę Article::setAuthorId()
, odwołalibyśmy się do niego w sekcji
setup
:
Pomocnik
Nette oprócz fabryk może generować accessory. Są to obiekty z metodą get()
, która zwraca określoną
usługę z kontenera DI. Wielokrotne wywołanie get()
wciąż zwraca tę samą instancję.
Accessory zapewniają leniwe ładowanie zależności. Rozważmy klasę, która zapisuje błędy do specjalnej bazy danych.
Gdyby ta klasa miała połączenie z bazą danych przekazywane jako zależność przez konstruktor, połączenie zawsze
musiałoby zostać nawiązane, choć w praktyce rzadko występowałby błąd, a więc przez większość czasu połączenie
pozostawałoby nieużywane. Więc zamiast tego klasa przekazuje accessor i tylko wtedy, gdy jego get()
jest
wywoływany, obiekt bazy danych zostaje utworzony:
Jak stworzyć accessora? Wystarczy napisać interfejs, a Nette DI wygeneruje jego implementację. Interfejs musi mieć
dokładnie jedną metodę o nazwie get
i deklarować typ zwrotny:
Dodaj accessor do pliku konfiguracyjnego, który określa również usługę, którą zwróci:
Ponieważ accessor zwraca usługę typu PDO
i w konfiguracji jest tylko jedna taka usługa, zwróci tę usługę.
Jeśli istnieje więcej usług tego typu, określ usługę, która ma być zwrócona przez nazwę, np.
- PDOAccessor(@db1)
.
Wiele fabryk/akcesoriów
Dotychczas nasze fabryki i accessory były w stanie wyprodukować lub zwrócić tylko jeden obiekt. Jednak bardzo łatwo jest
stworzyć wiele fabryk połączonych z accessorami. Interfejs takiej klasy będzie zawierał dowolną liczbę metod o nazwach
create<name>()
a get<name>()
, np:
Więc zamiast przekazywać kilka wygenerowanych fabryk i accessorów, przekazujemy jedną bardziej złożoną fabrykę, która może zrobić więcej.
Alternatywnie można użyć get()
z parametrem zamiast wielu metod:
W tym przypadku MultiFactory::getArticle()
robi to samo, co MultiFactoryAlt::get('article')
.
Alternatywna składnia ma jednak kilka wad. Nie jest jasne, które wartości $name
są obsługiwane, a typ zwracany
nie może być określony w interfejsie, gdy używanych jest wiele różnych wartości $name
.
Definicja według listy
W ten sposób można zdefiniować wiele fabryk w konfiguracji:
Lub w definicji fabryki możemy odwołać się do istniejących usług za pomocą referencji:
Definicja według znaczników
Druga opcja to użycie tagów do definicji: