Ορισμός υπηρεσιών

Η διαμόρφωση είναι το μέρος όπου διδάσκουμε στο DI container πώς να συναρμολογεί τις επιμέρους υπηρεσίες και πώς να τις συνδέει με άλλες εξαρτήσεις. Το Nette παρέχει έναν πολύ σαφή και κομψό τρόπο για να το πετύχουμε αυτό.

Η ενότητα services στο αρχείο διαμόρφωσης μορφής NEON είναι το μέρος όπου ορίζουμε τις δικές μας υπηρεσίες και τις διαμορφώσεις τους. Ας δούμε ένα απλό παράδειγμα ορισμού μιας υπηρεσίας με όνομα database, η οποία αντιπροσωπεύει μια παρουσία της κλάσης PDO:

services:
	database: PDO('sqlite::memory:')

Η παραπάνω διαμόρφωση θα οδηγήσει στην ακόλουθη μέθοδο factory στο DI container:

public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}

Τα ονόματα των υπηρεσιών μας επιτρέπουν να αναφερόμαστε σε αυτές σε άλλα μέρη του αρχείου διαμόρφωσης, με τη μορφή @ονομαΥπηρεσιας. Εάν δεν χρειάζεται να ονομάσουμε την υπηρεσία, μπορούμε απλά να χρησιμοποιήσουμε μόνο μια παύλα:

services:
	- PDO('sqlite::memory:')

Για να λάβουμε μια υπηρεσία από το DI container, μπορούμε να χρησιμοποιήσουμε τη μέθοδο getService() με το όνομα της υπηρεσίας ως παράμετρο, ή τη μέθοδο getByType() με τον τύπο της υπηρεσίας:

$database = $container->getService('database');
$database = $container->getByType(PDO::class);

Δημιουργία υπηρεσίας

Συνήθως δημιουργούμε μια υπηρεσία απλά δημιουργώντας μια παρουσία μιας συγκεκριμένης κλάσης. Για παράδειγμα:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

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

services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...

Το κλειδί create έχει ένα alias factory, και οι δύο παραλλαγές είναι συνηθισμένες στην πράξη. Ωστόσο, συνιστούμε τη χρήση του create.

Τα ορίσματα του κατασκευαστή ή της μεθόδου δημιουργίας μπορούν εναλλακτικά να γραφτούν στο κλειδί arguments:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

Οι υπηρεσίες δεν χρειάζεται να δημιουργούνται μόνο με την απλή δημιουργία μιας παρουσίας κλάσης, μπορούν επίσης να είναι το αποτέλεσμα της κλήσης στατικών μεθόδων ή μεθόδων άλλων υπηρεσιών:

services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()

Σημειώστε ότι για λόγους απλότητας, αντί για -> χρησιμοποιείται ::, δείτε Εκφραστικά μέσα. Θα δημιουργηθούν αυτές οι μέθοδοι factory:

public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}

Το DI container πρέπει να γνωρίζει τον τύπο της δημιουργημένης υπηρεσίας. Εάν δημιουργούμε μια υπηρεσία χρησιμοποιώντας μια μέθοδο που δεν έχει καθορισμένο τύπο επιστροφής, πρέπει να δηλώσουμε ρητά αυτόν τον τύπο στη διαμόρφωση:

services:
	database:
		create: DatabaseFactory::create()
		type: PDO

Ορίσματα

Στον κατασκευαστή και τις μεθόδους παραδίδουμε ορίσματα με τρόπο πολύ παρόμοιο με την ίδια την PHP:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Για καλύτερη αναγνωσιμότητα, μπορούμε να αναπτύξουμε τα ορίσματα σε ξεχωριστές γραμμές. Σε αυτή την περίπτωση, η χρήση κομμάτων είναι προαιρετική:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)

Μπορείτε επίσης να ονομάσετε τα ορίσματα και δεν χρειάζεται να ανησυχείτε για τη σειρά τους:

services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)

Εάν θέλετε να παραλείψετε ορισμένα ορίσματα και να χρησιμοποιήσετε την προεπιλεγμένη τους τιμή ή να εισαγάγετε μια υπηρεσία χρησιμοποιώντας autowiring, χρησιμοποιήστε την κάτω παύλα:

services:
	foo: Foo(_, %appDir%)

Ως ορίσματα μπορούν να παραδοθούν υπηρεσίες, να χρησιμοποιηθούν παράμετροι και πολλά άλλα, δείτε Εκφραστικά μέσα.

Setup

Στην ενότητα setup ορίζουμε τις μεθόδους που πρέπει να κληθούν κατά τη δημιουργία της υπηρεσίας.

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

Αυτό θα έμοιαζε έτσι στην PHP:

public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}

Εκτός από την κλήση μεθόδων, μπορούν επίσης να παραδοθούν τιμές σε properties. Υποστηρίζεται επίσης η προσθήκη ενός στοιχείου σε έναν πίνακα, το οποίο πρέπει να γραφτεί σε εισαγωγικά για να μην συγκρούεται με τη σύνταξη NEON:

services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]

Αυτό θα έμοιαζε ως εξής στον κώδικα PHP:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}

Στο setup, ωστόσο, μπορούν να κληθούν και στατικές μέθοδοι ή μέθοδοι άλλων υπηρεσιών. Εάν χρειάζεται να παραδώσετε την τρέχουσα υπηρεσία ως όρισμα, δηλώστε την ως @self:

services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)

Σημειώστε ότι για λόγους απλότητας, αντί για -> χρησιμοποιείται ::, δείτε Εκφραστικά μέσα. Θα δημιουργηθεί μια τέτοια μέθοδος factory:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}

Εκφραστικά μέσα

Το Nette DI μας δίνει εξαιρετικά πλούσια εκφραστικά μέσα, με τα οποία μπορούμε να γράψουμε σχεδόν οτιδήποτε. Στα αρχεία διαμόρφωσης μπορούμε έτσι να χρησιμοποιούμε παραμέτρους:

# παράμετρος
%wwwDir%

# τιμή παραμέτρου κάτω από κλειδί
%mailer.user%

# παράμετρος μέσα σε string
'%wwwDir%/images'

Επίσης, να δημιουργούμε αντικείμενα, να καλούμε μεθόδους και συναρτήσεις:

# δημιουργία αντικειμένου
DateTime()

# κλήση στατικής μεθόδου
Collator::create(%locale%)

# κλήση συνάρτησης PHP
::getenv(DB_USER)

Να αναφερόμαστε σε υπηρεσίες είτε με το όνομά τους είτε με τον τύπο τους:

# υπηρεσία βάσει ονόματος
@database

# υπηρεσία βάσει τύπου
@Nette\Database\Connection

Να χρησιμοποιούμε first-class callable syntax:

# δημιουργία callback, αντίστοιχο του [@user, logout]
@user::logout(...)

Να χρησιμοποιούμε σταθερές:

# σταθερά κλάσης
FilesystemIterator::SKIP_DOTS

# καθολική σταθερά λαμβάνεται με τη συνάρτηση PHP constant()
::constant(PHP_VERSION)

Οι κλήσεις μεθόδων μπορούν να αλυσιδωθούν όπως στην PHP. Απλώς για λόγους απλότητας, αντί για -> χρησιμοποιείται :::

DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()

Αυτές τις εκφράσεις μπορείτε να τις χρησιμοποιείτε οπουδήποτε, κατά τη δημιουργία υπηρεσιών, στα ορίσματα, στην ενότητα setup ή στις παραμέτρους:

parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )

Ειδικές συναρτήσεις

Στα αρχεία διαμόρφωσης μπορείτε να χρησιμοποιείτε αυτές τις ειδικές συναρτήσεις:

  • not() άρνηση της τιμής
  • bool(), int(), float(), string() μετατροπή τύπου χωρίς απώλειες στον καθορισμένο τύπο
  • typed() δημιουργεί έναν πίνακα όλων των υπηρεσιών του καθορισμένου τύπου
  • tagged() δημιουργεί έναν πίνακα όλων των υπηρεσιών με το δεδομένο tag
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Σε αντίθεση με την κλασική μετατροπή τύπου στην PHP, όπως π.χ. (int), η μετατροπή τύπου χωρίς απώλειες θα προκαλέσει εξαίρεση για μη αριθμητικές τιμές.

Η συνάρτηση typed() δημιουργεί έναν πίνακα όλων των υπηρεσιών του δεδομένου τύπου (κλάση ή interface). Παραλείπει τις υπηρεσίες που έχουν απενεργοποιημένο το autowiring. Μπορούν να δηλωθούν και περισσότεροι τύποι διαχωρισμένοι με κόμμα.

services:
	- BarsDependent( typed(Bar) )

Μπορείτε επίσης να παραδώσετε αυτόματα έναν πίνακα υπηρεσιών ενός συγκεκριμένου τύπου ως όρισμα χρησιμοποιώντας autowiring.

Η συνάρτηση tagged() στη συνέχεια δημιουργεί έναν πίνακα όλων των υπηρεσιών με ένα συγκεκριμένο tag. Και εδώ μπορείτε να καθορίσετε περισσότερα tags διαχωρισμένα με κόμμα.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

Το κλειδί autowired επιτρέπει την επίδραση στη συμπεριφορά του autowiring για μια συγκεκριμένη υπηρεσία. Για λεπτομέρειες, δείτε το κεφάλαιο για το autowiring.

services:
	foo:
		create: Foo
		autowired: false     # η υπηρεσία foo εξαιρείται από το autowiring

Lazy υπηρεσίες

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

services:
	foo:
		create: Foo
		lazy: false

Όταν μια υπηρεσία ορίζεται ως lazy, κατά την αίτησή της από το DI container, λαμβάνουμε ένα ειδικό αντικείμενο υποκατάστατο. Αυτό φαίνεται και συμπεριφέρεται το ίδιο με την πραγματική υπηρεσία, αλλά η πραγματική αρχικοποίηση (κλήση του κατασκευαστή και του setup) πραγματοποιείται μόνο κατά την πρώτη κλήση οποιασδήποτε μεθόδου ή property της.

Το Lazy loading μπορεί να χρησιμοποιηθεί μόνο για κλάσεις χρήστη, όχι για εσωτερικές κλάσεις PHP. Απαιτεί PHP 8.4 ή νεότερη έκδοση.

Tags

Τα tags χρησιμοποιούνται για την προσθήκη συμπληρωματικών πληροφοριών στις υπηρεσίες. Μπορείτε να προσθέσετε ένα ή περισσότερα tags σε μια υπηρεσία:

services:
	foo:
		create: Foo
		tags:
			- cached

Τα tags μπορούν επίσης να φέρουν τιμές:

services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event

Για να λάβετε όλες τις υπηρεσίες με συγκεκριμένα tags, μπορείτε να χρησιμοποιήσετε τη συνάρτηση tagged():

services:
	- LoggersDependent( tagged(logger) )

Στο DI container, μπορείτε να λάβετε τα ονόματα όλων των υπηρεσιών με ένα συγκεκριμένο tag χρησιμοποιώντας τη μέθοδο findByTag():

$names = $container->findByTag('logger');
// Το $names είναι ένας πίνακας που περιέχει το όνομα της υπηρεσίας και την τιμή του tag
// π.χ. ['foo' => 'monolog.logger.event', ...]

Λειτουργία Inject

Με τη χρήση της σημαίας inject: true ενεργοποιείται η παράδοση εξαρτήσεων μέσω δημόσιων μεταβλητών με την annotation inject και μεθόδων inject*().

services:
	articles:
		create: App\Model\Articles
		inject: true

Στην προεπιλεγμένη ρύθμιση, το inject ενεργοποιείται μόνο για τους presenters.

Τροποποίηση υπηρεσιών

Το DI container περιέχει πολλές υπηρεσίες που έχουν προστεθεί μέσω ενσωματωμένης ή επέκτασης χρήστη. Μπορείτε να τροποποιήσετε τους ορισμούς αυτών των υπηρεσιών απευθείας στη διαμόρφωση. Για παράδειγμα, μπορείτε να αλλάξετε την κλάση της υπηρεσίας application.application, η οποία είναι συνήθως Nette\Application\Application, σε άλλη:

services:
	application.application:
		create: MyApplication
		alteration: true

Η σημαία alteration είναι πληροφοριακή και λέει ότι απλώς τροποποιούμε μια υπάρχουσα υπηρεσία.

Μπορούμε επίσης να συμπληρώσουμε το setup:

services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]

Κατά την αντικατάσταση μιας υπηρεσίας, μπορεί να θέλουμε να αφαιρέσουμε τα αρχικά ορίσματα, στοιχεία setup ή tags, για τα οποία χρησιμοποιείται το reset:

services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags

Εάν θέλετε να αφαιρέσετε μια υπηρεσία που προστέθηκε από επέκταση, μπορείτε να το κάνετε ως εξής:

services:
	cache.journal: false
έκδοση: 3.x