Δημιουργία επεκτάσεων για το Nette DI
Η δημιουργία ενός δοχείου DI εκτός από τα αρχεία ρυθμίσεων
επηρεάζει και τα λεγόμενα επεκτάσεις. Τις ενεργοποιούμε στο αρχείο
διαμόρφωσης στην ενότητα extensions
.
Έτσι προσθέτουμε την επέκταση που αντιπροσωπεύεται από την κλάση
BlogExtension
με το όνομα blog
:
extensions:
blog: BlogExtension
Κάθε επέκταση μεταγλωττιστή κληρονομεί από την Nette\DI\CompilerExtension και μπορεί να υλοποιήσει τις ακόλουθες μεθόδους που καλούνται κατά τη διάρκεια της μεταγλώττισης του DI:
- getConfigSchema()
- loadConfiguration()
- beforeCompile()
- afterCompile()
getConfigSchema()
Αυτή η μέθοδος καλείται πρώτη. Ορίζει το σχήμα που χρησιμοποιείται για την επικύρωση των παραμέτρων διαμόρφωσης.
Οι επεκτάσεις διαμορφώνονται σε ένα τμήμα του οποίου το όνομα είναι
το ίδιο με αυτό στο οποίο προστέθηκε η επέκταση, π.χ. blog
.
# ίδιο όνομα με την επέκτασή μου
blog:
postsPerPage: 10
comments: false
Θα ορίσουμε ένα σχήμα που θα περιγράφει όλες τις επιλογές διαμόρφωσης, συμπεριλαμβανομένων των τύπων τους, των αποδεκτών τιμών και ενδεχομένως των προεπιλεγμένων τιμών:
use Nette\Schema\Expect;
class BlogExtension extends Nette\DI\CompilerExtension
{
public function getConfigSchema(): Nette\Schema\Schema
{
return Expect::structure([
'postsPerPage' => Expect::int(),
'allowComments' => Expect::bool()->default(true),
]);
}
}
Ανατρέξτε στο Σχήμα για τεκμηρίωση.
Επιπλέον, μπορείτε να καθορίσετε ποιες επιλογές μπορούν να είναι δυναμικές χρησιμοποιώντας
dynamic()
, για παράδειγμα Expect::int()->dynamic()
.
Έχουμε πρόσβαση στη διαμόρφωση μέσω του $this->config
, το οποίο
είναι ένα αντικείμενο stdClass
:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$num = $this->config->postPerPage;
if ($this->config->allowComments) {
// ...
}
}
}
loadConfiguration()
Αυτή η μέθοδος χρησιμοποιείται για την προσθήκη υπηρεσιών στο δοχείο. Αυτό γίνεται με τη μέθοδο Nette\DI\ContainerBuilder:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$builder->addDefinition($this->prefix('articles'))
->setFactory(App\Model\HomepageArticles::class, ['@connection']) // ή setCreator()
->addSetup('setLogger', ['@logger']);
}
}
Η σύμβαση είναι να προτάσσονται οι υπηρεσίες που προστίθενται από
μια επέκταση με το όνομά της, ώστε να μην προκύπτουν συγκρούσεις
ονομάτων. Αυτό γίνεται με το prefix()
, οπότε αν η επέκταση
ονομάζεται “blog”, η υπηρεσία θα ονομάζεται blog.articles
.
Αν χρειαστεί να μετονομάσουμε μια υπηρεσία, μπορούμε να
δημιουργήσουμε ένα ψευδώνυμο με το αρχικό της όνομα για να
διατηρήσουμε την προς τα πίσω συμβατότητα. Ομοίως αυτό κάνει η Nette για
παράδειγμα για το routing.router
, το οποίο είναι επίσης διαθέσιμο με το
προηγούμενο όνομα router
.
$builder->addAlias('router', 'routing.router');
Ανάκτηση υπηρεσιών από ένα αρχείο
Μπορούμε να δημιουργήσουμε υπηρεσίες χρησιμοποιώντας το API του
ContainerBuilder, αλλά μπορούμε επίσης να τις προσθέσουμε μέσω του γνωστού
αρχείου ρυθμίσεων NEON και της ενότητας services
. Το πρόθεμα
@extension
αντιπροσωπεύει την τρέχουσα επέκταση.
services:
articles:
create: MyBlog\ArticlesModel(@connection)
comments:
create: MyBlog\CommentsModel(@connection, @extension.articles)
articlesList:
create: MyBlog\Components\ArticlesList(@extension.articles)
Θα προσθέσουμε υπηρεσίες με αυτόν τον τρόπο:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
// φορτώνει το αρχείο ρυθμίσεων για την επέκταση
$this->compiler->loadDefinitionsFromConfig(
$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
);
}
}
beforeCompile()
Η μέθοδος καλείται όταν ο περιέκτης περιέχει όλες τις υπηρεσίες που
έχουν προστεθεί από τις επιμέρους επεκτάσεις στις μεθόδους
loadConfiguration
καθώς και τα αρχεία ρυθμίσεων του χρήστη. Σε αυτή τη
φάση της συναρμολόγησης, μπορούμε στη συνέχεια να τροποποιήσουμε τους
ορισμούς των υπηρεσιών ή να προσθέσουμε συνδέσμους μεταξύ τους.
Μπορείτε να χρησιμοποιήσετε τη μέθοδο findByTag()
για την αναζήτηση
υπηρεσιών με βάση τις ετικέτες ή τη μέθοδο findByType()
για την
αναζήτηση με βάση την κλάση ή τη διεπαφή.
class BlogExtension extends Nette\DI\CompilerExtension
{
public function beforeCompile()
{
$builder = $this->getContainerBuilder();
foreach ($builder->findByTag('logaware') as $serviceName => $tagValue) {
$builder->getDefinition($serviceName)->addSetup('setLogger');
}
}
}
afterCompile()
Σε αυτή τη φάση, η κλάση εμπορευματοκιβωτίου έχει ήδη δημιουργηθεί ως αντικείμενο ClassType, περιέχει όλες τις μεθόδους που δημιουργεί η υπηρεσία και είναι έτοιμη για προσωρινή αποθήκευση ως αρχείο PHP. Μπορούμε ακόμα να επεξεργαστούμε τον κώδικα της κλάσης που προκύπτει σε αυτό το σημείο.
class BlogExtension extends Nette\DI\CompilerExtension
{
public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
$method = $class->getMethod('__construct');
// ...
}
}
$initialization
Ο διαμορφωτής καλεί τον κώδικα αρχικοποίησης μετά τη δημιουργία του περιέκτη, ο
οποίος δημιουργείται με εγγραφή σε ένα αντικείμενο
$this->initialization
χρησιμοποιώντας τη μέθοδο addBody().
Θα δείξουμε ένα παράδειγμα για το πώς να ξεκινήσετε μια συνεδρία ή να
ξεκινήσετε υπηρεσίες που έχουν την ετικέτα run
χρησιμοποιώντας
κώδικα αρχικοποίησης:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
// αυτόματη εκκίνηση συνεδρίας
if ($this->config->session->autoStart) {
$this->initialization->addBody('$this->getService("session")->start()');
}
// οι υπηρεσίες με ετικέτα 'run' πρέπει να δημιουργούνται μετά την ενσάρκωση του δοχείου
$builder = $this->getContainerBuilder();
foreach ($builder->findByTag('run') as $name => $foo) {
$this->initialization->addBody('$this->getService(?);', [$name]);
}
}
}