Παραγόμενα εργοστάσια

Το Nette DI μπορεί να παράγει αυτόματα κώδικα εργοστασίων με βάση τη διεπαφή, γεγονός που σας απαλλάσσει από τη συγγραφή κώδικα.

Ένα εργοστάσιο είναι μια κλάση που δημιουργεί και διαμορφώνει αντικείμενα. Επομένως, τους μεταβιβάζει και τις εξαρτήσεις τους. Μην συγχέετε με το πρότυπο σχεδίασης factory method, το οποίο περιγράφει έναν συγκεκριμένο τρόπο χρήσης των εργοστασίων και δεν σχετίζεται με αυτό το θέμα.

Έχουμε δείξει πώς μοιάζει ένα τέτοιο εργοστάσιο στο εισαγωγικό κεφάλαιο:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

Το Nette DI μπορεί να παράγει αυτόματα κώδικα εργοστασίου. Το μόνο που έχετε να κάνετε είναι να δημιουργήσετε μια διεπαφή και το Nette DI θα δημιουργήσει μια υλοποίηση. Η διεπαφή πρέπει να έχει ακριβώς μία μέθοδο με το όνομα create και να δηλώνει έναν τύπο επιστροφής:

interface ArticleFactory
{
	function create(): Article;
}

Έτσι, το εργοστάσιο ArticleFactory έχει μια μέθοδο create που δημιουργεί αντικείμενα Article. Η κλάση Article μπορεί να μοιάζει, για παράδειγμα, με την ακόλουθη:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

Προσθέστε το εργοστάσιο στο αρχείο ρυθμίσεων:

services:
	- ArticleFactory

Το Nette DI θα δημιουργήσει την αντίστοιχη υλοποίηση του εργοστασίου.

Έτσι, στον κώδικα που χρησιμοποιεί το εργοστάσιο, ζητάμε το αντικείμενο μέσω διεπαφής και το Nette DI χρησιμοποιεί την υλοποίηση που παράγεται:

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// αφήστε το εργοστάσιο να δημιουργήσει ένα αντικείμενο
		$article = $this->articleFactory->create();
	}
}

Παραμετροποιημένο εργοστάσιο

Η μέθοδος του εργοστασίου create μπορεί να δέχεται παραμέτρους τις οποίες στη συνέχεια μεταβιβάζει στον κατασκευαστή. Για παράδειγμα, ας προσθέσουμε ένα αναγνωριστικό συγγραφέα άρθρου στην κλάση Article:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

Θα προσθέσουμε επίσης την παράμετρο στο εργοστάσιο:

interface ArticleFactory
{
	function create(int $authorId): Article;
}

Επειδή η παράμετρος στον κατασκευαστή και η παράμετρος στο εργοστάσιο έχουν το ίδιο όνομα, η Nette DI θα τις περάσει αυτόματα.

Προηγμένος ορισμός

Ο ορισμός μπορεί επίσης να γραφτεί σε μορφή πολλαπλών γραμμών χρησιμοποιώντας το πλήκτρο implement:

services:
	articleFactory:
		implement: ArticleFactory

Όταν γράφετε με αυτόν τον μακρύτερο τρόπο, είναι δυνατόν να παρέχετε πρόσθετα ορίσματα για τον κατασκευαστή στο κλειδί arguments και πρόσθετες ρυθμίσεις χρησιμοποιώντας το setup, όπως ακριβώς και για τις κανονικές υπηρεσίες.

Παράδειγμα: αν η μέθοδος create() δεν δεχόταν την παράμετρο $authorId, θα μπορούσαμε να καθορίσουμε μια σταθερή τιμή στη διαμόρφωση που θα περνούσε στον κατασκευαστή Article:

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

Ή, αντίθετα, αν η create() δεχόταν την παράμετρο $authorId αλλά δεν αποτελούσε μέρος του κατασκευαστή και περνούσε από τη μέθοδο Article::setAuthorId(), θα αναφερόμασταν σε αυτήν στην ενότητα setup:

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

Accessor

Εκτός από τα εργοστάσια, η Nette μπορεί επίσης να δημιουργήσει τους λεγόμενους accessors. Ο accessor είναι ένα αντικείμενο με τη μέθοδο get() που επιστρέφει μια συγκεκριμένη υπηρεσία από το δοχείο DI. Πολλαπλές κλήσεις get() θα επιστρέφουν πάντα το ίδιο παράδειγμα.

Οι accessors φέρνουν lazy-loading στις εξαρτήσεις. Ας έχουμε μια κλάση που καταγράφει σφάλματα σε μια ειδική βάση δεδομένων. Αν η σύνδεση της βάσης δεδομένων περνούσε ως εξάρτηση στον κατασκευαστή της, η σύνδεση θα έπρεπε να δημιουργείται πάντα, αν και θα χρησιμοποιούνταν μόνο σπάνια όταν εμφανίζεται ένα σφάλμα, οπότε η σύνδεση θα έμενε ως επί το πλείστον αχρησιμοποίητη. Αντ' αυτού, η κλάση μπορεί να περάσει έναν accessor και όταν καλείται η μέθοδος get(), μόνο τότε δημιουργείται το αντικείμενο της βάσης δεδομένων:

Πώς να δημιουργήσετε έναν προσπελάτη; Γράψτε μόνο μια διεπαφή και η Nette DI θα δημιουργήσει την υλοποίηση. Η διασύνδεση πρέπει να έχει ακριβώς μία μέθοδο που ονομάζεται get και πρέπει να δηλώνει τον τύπο επιστροφής:

interface PDOAccessor
{
	function get(): PDO;
}

Προσθέστε την προσπέλαση στο αρχείο ρυθμίσεων μαζί με τον ορισμό της υπηρεσίας που θα επιστρέψει η προσπέλαση:

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

Ο accessor επιστρέφει μια υπηρεσία τύπου PDO και επειδή υπάρχει μόνο μια τέτοια υπηρεσία στη διαμόρφωση, ο accessor θα την επιστρέψει. Με πολλαπλές διαμορφωμένες υπηρεσίες αυτού του τύπου μπορείτε να καθορίσετε ποια θα επιστραφεί χρησιμοποιώντας το όνομά της, για παράδειγμα - PDOAccessor(@db1).

Multifactory/Accessor

Μέχρι στιγμής, τα εργοστάσια και οι accessors μπορούσαν να δημιουργήσουν ή να επιστρέψουν μόνο ένα αντικείμενο. Μπορεί επίσης να δημιουργηθεί ένα multifactory σε συνδυασμό με έναν accessor. Η διεπαφή μιας τέτοιας κλάσης multifactory μπορεί να αποτελείται από πολλαπλές μεθόδους που ονομάζονται create<name>() και get<name>(), για παράδειγμα:

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

Αντί να περνάτε πολλαπλά παραγόμενα εργοστάσια και accessors, μπορείτε να περνάτε μόνο ένα σύνθετο multifactory.

Εναλλακτικά, μπορείτε να χρησιμοποιήσετε το get() με μια παράμετρο αντί για πολλαπλές μεθόδους:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

Σε αυτή την περίπτωση, το MultiFactory::getArticle() κάνει το ίδιο πράγμα με το MultiFactoryAlt::get('article'). Ωστόσο, η εναλλακτική σύνταξη έχει μερικά μειονεκτήματα. Δεν είναι σαφές ποιες τιμές $name υποστηρίζονται και ο τύπος επιστροφής δεν μπορεί να καθοριστεί στη διεπαφή όταν χρησιμοποιούνται πολλές διαφορετικές τιμές $name.

Ορισμός με λίστα

Αυτός ο τρόπος μπορεί να χρησιμοποιηθεί για τον ορισμό ενός πολλαπλού εργοστασίου στη διαμόρφωση:

services:
	- MultiFactory(
		article: Article                      # defines createArticle()
		db: PDO(%dsn%, %user%, %password%)    # defines getDb()
	)

Ή, στον ορισμό του εργοστασίου, μπορούμε να αναφερθούμε σε υπάρχουσες υπηρεσίες χρησιμοποιώντας μια αναφορά:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # defines createArticle()
		db: @\PDO            # defines getDb()
	)

Ορισμός με ετικέτες

Μια άλλη επιλογή για τον ορισμό ενός multifactory είναι η χρήση ετικετών:

services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
έκδοση: 3.x