Nette Documentation Preview

syntax
Δημιουργία επεκτάσεων για το Nette DI
*************************************

.[perex]
Η δημιουργία ενός δοχείου DI εκτός από τα αρχεία ρυθμίσεων επηρεάζει και τα λεγόμενα *επεκτάσεις*. Τις ενεργοποιούμε στο αρχείο διαμόρφωσης στην ενότητα `extensions`.

Έτσι προσθέτουμε την επέκταση που αντιπροσωπεύεται από την κλάση `BlogExtension` με το όνομα `blog`:

```neon
extensions:
	blog: BlogExtension
```

Κάθε επέκταση μεταγλωττιστή κληρονομεί από την [api:Nette\DI\CompilerExtension] και μπορεί να υλοποιήσει τις ακόλουθες μεθόδους που καλούνται κατά τη διάρκεια της μεταγλώττισης του DI:

1. getConfigSchema()
2. loadConfiguration()
3. beforeCompile()
4. afterCompile()


getConfigSchema() .[method]
===========================

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

Οι επεκτάσεις διαμορφώνονται σε ένα τμήμα του οποίου το όνομα είναι το ίδιο με αυτό στο οποίο προστέθηκε η επέκταση, π.χ. `blog`.

```neon
# ίδιο όνομα με την επέκτασή μου
blog:
	postsPerPage: 10
	comments: false
```

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

```php
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),
		]);
	}
}
```

Ανατρέξτε στο [Σχήμα |schema:] για τεκμηρίωση. Επιπλέον, μπορείτε να καθορίσετε ποιες επιλογές μπορούν να είναι [δυναμικές |application:bootstrap#Dynamic Parameters] χρησιμοποιώντας `dynamic()`, για παράδειγμα `Expect::int()->dynamic()`.

Έχουμε πρόσβαση στη διαμόρφωση μέσω του `$this->config`, το οποίο είναι ένα αντικείμενο `stdClass`:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$num = $this->config->postPerPage;
		if ($this->config->allowComments) {
			// ...
		}
	}
}
```


loadConfiguration() .[method]
=============================

Αυτή η μέθοδος χρησιμοποιείται για την προσθήκη υπηρεσιών στο δοχείο. Αυτό γίνεται με τη μέθοδο [api:Nette\DI\ContainerBuilder]:

```php
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`.

```php
$builder->addAlias('router', 'routing.router');
```


Ανάκτηση υπηρεσιών από ένα αρχείο .[#toc-retrieve-services-from-a-file]
-----------------------------------------------------------------------

Μπορούμε να δημιουργήσουμε υπηρεσίες χρησιμοποιώντας το API του ContainerBuilder, αλλά μπορούμε επίσης να τις προσθέσουμε μέσω του γνωστού αρχείου ρυθμίσεων NEON και της ενότητας `services`. Το πρόθεμα `@extension` αντιπροσωπεύει την τρέχουσα επέκταση.

```neon
services:
	articles:
		create: MyBlog\ArticlesModel(@connection)

	comments:
		create: MyBlog\CommentsModel(@connection, @extension.articles)

	articlesList:
		create: MyBlog\Components\ArticlesList(@extension.articles)
```

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

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();

		// φορτώνει το αρχείο ρυθμίσεων για την επέκταση
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}
```


beforeCompile() .[method]
=========================

Η μέθοδος καλείται όταν ο περιέκτης περιέχει όλες τις υπηρεσίες που έχουν προστεθεί από τις επιμέρους επεκτάσεις στις μεθόδους `loadConfiguration` καθώς και τα αρχεία ρυθμίσεων του χρήστη. Σε αυτή τη φάση της συναρμολόγησης, μπορούμε στη συνέχεια να τροποποιήσουμε τους ορισμούς των υπηρεσιών ή να προσθέσουμε συνδέσμους μεταξύ τους. Μπορείτε να χρησιμοποιήσετε τη μέθοδο `findByTag()` για την αναζήτηση υπηρεσιών με βάση τις ετικέτες ή τη μέθοδο `findByType()` για την αναζήτηση με βάση την κλάση ή τη διεπαφή.

```php
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() .[method]
========================

Σε αυτή τη φάση, η κλάση εμπορευματοκιβωτίου έχει ήδη δημιουργηθεί ως αντικείμενο [ClassType |php-generator:#classes], περιέχει όλες τις μεθόδους που δημιουργεί η υπηρεσία και είναι έτοιμη για προσωρινή αποθήκευση ως αρχείο PHP. Μπορούμε ακόμα να επεξεργαστούμε τον κώδικα της κλάσης που προκύπτει σε αυτό το σημείο.

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function afterCompile(Nette\PhpGenerator\ClassType $class)
	{
		$method = $class->getMethod('__construct');
		// ...
	}
}
```


$initialization .[wiki-method]
==============================

Ο διαμορφωτής καλεί τον κώδικα αρχικοποίησης μετά τη [δημιουργία του περιέκτη |application:bootstrap#index.php], ο οποίος δημιουργείται με εγγραφή σε ένα αντικείμενο `$this->initialization` χρησιμοποιώντας τη [μέθοδο addBody() |php-generator:#method-and-function-body].

Θα δείξουμε ένα παράδειγμα για το πώς να ξεκινήσετε μια συνεδρία ή να ξεκινήσετε υπηρεσίες που έχουν την ετικέτα `run` χρησιμοποιώντας κώδικα αρχικοποίησης:

```php
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]);
		}
	}
}
```

Δημιουργία επεκτάσεων για το Nette DI

Η δημιουργία ενός δοχείου DI εκτός από τα αρχεία ρυθμίσεων επηρεάζει και τα λεγόμενα επεκτάσεις. Τις ενεργοποιούμε στο αρχείο διαμόρφωσης στην ενότητα extensions.

Έτσι προσθέτουμε την επέκταση που αντιπροσωπεύεται από την κλάση BlogExtension με το όνομα blog:

extensions:
	blog: BlogExtension

Κάθε επέκταση μεταγλωττιστή κληρονομεί από την Nette\DI\CompilerExtension και μπορεί να υλοποιήσει τις ακόλουθες μεθόδους που καλούνται κατά τη διάρκεια της μεταγλώττισης του DI:

  1. getConfigSchema()
  2. loadConfiguration()
  3. beforeCompile()
  4. 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]);
		}
	}
}