Fabrici generate
Nette DI poate genera automat codul fabricii pe baza interfeței, ceea ce vă scutește de scrierea de cod.
O fabrică este o clasă care creează și configurează obiecte. Prin urmare, le transmite și dependențele acestora. Vă rugăm să nu faceți confuzie cu modelul de proiectare factory method, care descrie un mod specific de utilizare a fabricilor și nu are legătură cu acest subiect.
Am arătat cum arată o astfel de fabrică în capitolul introductiv:
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
Nette DI poate genera automat cod de fabrică. Tot ce trebuie să faceți este să creați o interfață și Nette DI va
genera o implementare. Interfața trebuie să aibă exact o metodă numită create
și să declare un tip de
returnare:
interface ArticleFactory
{
function create(): Article;
}
Astfel, fabrica ArticleFactory
are o metodă create
care creează obiecte Article
.
Clasa Article
ar putea arăta, de exemplu, după cum urmează:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
Adăugați fabrica în fișierul de configurare:
services:
- ArticleFactory
Nette DI va genera implementarea fabricii corespunzătoare.
Astfel, în codul care utilizează fabrica, solicităm obiectul prin interfață, iar Nette DI utilizează implementarea generată:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// permiteți fabricii să creeze un obiect
$article = $this->articleFactory->create();
}
}
Fabrica parametrizată
Metoda factory create
poate accepta parametri pe care îi transmite apoi constructorului. De exemplu, să
adăugăm un ID de autor de articol la clasa Article
:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
Vom adăuga, de asemenea, parametrul la fabrică:
interface ArticleFactory
{
function create(int $authorId): Article;
}
Deoarece parametrul din constructor și parametrul din fabrică au același nume, Nette DI le va trece automat.
Definiție avansată
Definiția poate fi scrisă și sub formă de mai multe rânduri cu ajutorul tastei implement
:
services:
articleFactory:
implement: ArticleFactory
Atunci când se scrie în acest mod mai lung, este posibil să se furnizeze argumente suplimentare pentru constructor în cheia
arguments
și o configurație suplimentară folosind setup
, la fel ca pentru serviciile normale.
Exemplu: dacă metoda create()
nu acceptă parametrul $authorId
, am putea specifica o valoare fixă
în configurație, care va fi transmisă constructorului Article
:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
Sau, dimpotrivă, dacă create()
accepta parametrul $authorId
, dar acesta nu făcea parte din
constructor și era transmis de metoda Article::setAuthorId()
, ne-am putea referi la el în secțiunea
setup
:
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
Accesor
În afară de fabrici, Nette poate genera și așa numiții accesori. Accesorul este un obiect cu metoda get()
care returnează un anumit serviciu de la containerul DI. Mai multe apeluri la get()
vor returna întotdeauna
aceeași instanță.
Accesorii permit încărcarea leneșă a dependențelor. Să avem o clasă care înregistrează erorile într-o bază de date
specială. Dacă conexiunea la baza de date ar fi trecută ca dependență în constructorul său, conexiunea ar trebui să fie
mereu creată, deși ar fi folosită doar rareori când apare o eroare, astfel încât conexiunea ar rămâne în mare parte
neutilizată. În schimb, clasa poate trece un accesor și, atunci când este apelată metoda get()
, numai atunci
este creat obiectul bazei de date:
Cum se creează un accesor? Scrieți doar o interfață, iar Nette DI va genera implementarea. Interfața trebuie să aibă
exact o metodă numită get
și trebuie să declare tipul de returnare:
interface PDOAccessor
{
function get(): PDO;
}
Adăugați accesorul în fișierul de configurare împreună cu definiția serviciului pe care accesorul îl va returna:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
Accesorul returnează un serviciu de tip PDO
și, deoarece există un singur astfel de serviciu în configurație,
accesorul îl va returna. În cazul în care există mai multe servicii configurate de acest tip, puteți specifica care dintre
ele trebuie returnat folosind numele său, de exemplu - PDOAccessor(@db1)
.
Multifactory/Accesor
Până acum, fabricile și accesorii puteau crea sau returna doar un singur obiect. Se poate crea, de asemenea, un multifactory
combinat cu un accesor. Interfața unei astfel de clase multifactory poate consta din mai multe metode numite
create<name>()
și get<name>()
, de exemplu:
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
În loc să transmiteți mai multe fabrici și accesori generați, puteți transmite doar o singură multifactorie complexă.
Alternativ, puteți utiliza get()
cu un parametru în loc de mai multe metode:
interface MultiFactoryAlt
{
function get($name): PDO;
}
În acest caz, MultiFactory::getArticle()
face același lucru ca și MultiFactoryAlt::get('article')
.
Cu toate acestea, sintaxa alternativă are câteva dezavantaje. Nu este clar ce valori $name
sunt acceptate, iar
tipul de returnare nu poate fi specificat în interfață atunci când se utilizează mai multe valori $name
diferite.
Definiție cu o listă
Acest mod poate fi utilizat pentru a defini o fabrică multiplă în configurație:
services:
- MultiFactory(
article: Article # defines createArticle()
db: PDO(%dsn%, %user%, %password%) # defines getDb()
)
Sau, în definiția fabricii, putem face referire la serviciile existente folosind o referință:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # defines createArticle()
db: @\PDO # defines getDb()
)
Definiție cu etichete
O altă opțiune de definire a unui multifactorial este utilizarea etichetelor:
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)