Nette Documentation Preview

syntax
Nette DI için Uzantı Oluşturma
******************************

.[perex]
DI konteynerinin oluşturulması, yapılandırma dosyalarının yanı sıra *uzantılar* olarak adlandırılanlar tarafından da etkilenir. Bunları yapılandırma dosyasında `extensions` bölümünde etkinleştiririz.

Bu şekilde, `BlogExtension` sınıfı tarafından temsil edilen uzantıyı `blog` adı altında ekleriz:

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

Her derleyici uzantısı [api:Nette\DI\CompilerExtension]'dan kalıtım alır ve DI konteynerinin oluşturulması sırasında sırayla çağrılan aşağıdaki metotları uygulayabilir:

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


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

Bu metot ilk olarak çağrılır. Yapılandırma parametrelerinin doğrulanması için şemayı tanımlar.

Uzantıyı, uzantının eklendiği adla aynı olan bölümde, yani `blog`'da yapılandırırız:

```neon
# uzantı adıyla aynı
blog:
	postsPerPage: 10
	allowComments: false
```

Tipleri, izin verilen değerleri ve isteğe bağlı olarak varsayılan değerleri de dahil olmak üzere tüm yapılandırma seçeneklerini açıklayan bir şema oluştururuz:

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

Dokümantasyonu [Schema |schema:] sayfasında bulabilirsiniz. Ayrıca, `dynamic()` kullanarak hangi seçeneklerin [dinamik |application:bootstrapping#Dinamik Parametreler] olabileceğini belirleyebilirsiniz, örn. `Expect::int()->dynamic()`.

Yapılandırmaya, bir `stdClass` nesnesi olan `$this->config` değişkeni aracılığıyla erişiriz:

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


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

Konteynere servis eklemek için kullanılır. Bunun için [api:Nette\DI\ContainerBuilder] kullanılır:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();
		$builder->addDefinition($this->prefix('articles'))
			->setFactory(App\Model\HomepageArticles::class, ['@connection']) // veya setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}
```

Kural, uzantı tarafından eklenen servisleri adıyla ön eklemektir, böylece isim çakışmaları olmaz. Bunu `prefix()` metodu yapar, yani uzantı adı `blog` ise, servis `blog.articles` adını taşır.

Bir servisi yeniden adlandırmamız gerekirse, geriye dönük uyumluluğu korumak için orijinal adla bir takma ad (alias) oluşturabiliriz. Nette bunu benzer şekilde yapar, örn. önceki adı `router` altında da mevcut olan `routing.router` servisi için.

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


Dosyadan Servis Yükleme
-----------------------

Servisleri yalnızca ContainerBuilder sınıfının API'sini kullanarak değil, aynı zamanda yapılandırma dosyasında `services` bölümünde kullanılan bilinen yazımla da oluşturabiliriz. `@extension` ön eki mevcut uzantıyı temsil eder.

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

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

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

Servisleri yükleriz:

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

		// uzantı için yapılandırma dosyasını yükleme
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}
```


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

Metot, konteynerin `loadConfiguration` metotlarında bireysel uzantılar tarafından eklenen tüm servisleri ve ayrıca kullanıcı yapılandırma dosyalarını içerdiği anda çağrılır. Bu derleme aşamasında, servis tanımlarını düzenleyebilir veya aralarındaki bağlantıları tamamlayabiliriz. Konteynerdeki servisleri etiketlere göre aramak için `findByTag()` metodunu, sınıfa veya arayüze göre aramak için ise `findByType()` metodunu kullanabiliriz.

```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]
========================

Bu aşamada, konteyner sınıfı zaten bir [ClassType |php-generator:#Sınıflar] nesnesi şeklinde üretilmiştir, servisleri oluşturan tüm metotları içerir ve önbelleğe yazılmaya hazırdır. Sonuç kodunu bu noktada hala düzenleyebiliriz.

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


$initialization .[method]
=========================

Configurator sınıfı, [konteyner oluşturulduktan sonra |application:bootstrapping#index.php] `$this->initialization` nesnesine [addBody() metodu |php-generator:#Metot ve Fonksiyon Gövdeleri] kullanılarak yazılan başlatma kodunu çağırır.

Örneğin, başlatma koduyla oturumu nasıl başlatacağımızı veya `run` etiketine sahip servisleri nasıl çalıştıracağımızı gösteren bir örnek:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// oturumun otomatik başlatılması
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// run etiketli servisler konteyner örneklendikten sonra oluşturulmalıdır
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}
```

Nette DI için Uzantı Oluşturma

DI konteynerinin oluşturulması, yapılandırma dosyalarının yanı sıra uzantılar olarak adlandırılanlar tarafından da etkilenir. Bunları yapılandırma dosyasında extensions bölümünde etkinleştiririz.

Bu şekilde, BlogExtension sınıfı tarafından temsil edilen uzantıyı blog adı altında ekleriz:

extensions:
	blog: BlogExtension

Her derleyici uzantısı Nette\DI\CompilerExtension'dan kalıtım alır ve DI konteynerinin oluşturulması sırasında sırayla çağrılan aşağıdaki metotları uygulayabilir:

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

getConfigSchema()

Bu metot ilk olarak çağrılır. Yapılandırma parametrelerinin doğrulanması için şemayı tanımlar.

Uzantıyı, uzantının eklendiği adla aynı olan bölümde, yani blog'da yapılandırırız:

# uzantı adıyla aynı
blog:
	postsPerPage: 10
	allowComments: false

Tipleri, izin verilen değerleri ve isteğe bağlı olarak varsayılan değerleri de dahil olmak üzere tüm yapılandırma seçeneklerini açıklayan bir şema oluştururuz:

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

Dokümantasyonu Schema sayfasında bulabilirsiniz. Ayrıca, dynamic() kullanarak hangi seçeneklerin dinamik olabileceğini belirleyebilirsiniz, örn. Expect::int()->dynamic().

Yapılandırmaya, bir stdClass nesnesi olan $this->config değişkeni aracılığıyla erişiriz:

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

loadConfiguration()

Konteynere servis eklemek için kullanılır. Bunun için Nette\DI\ContainerBuilder kullanılır:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();
		$builder->addDefinition($this->prefix('articles'))
			->setFactory(App\Model\HomepageArticles::class, ['@connection']) // veya setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}

Kural, uzantı tarafından eklenen servisleri adıyla ön eklemektir, böylece isim çakışmaları olmaz. Bunu prefix() metodu yapar, yani uzantı adı blog ise, servis blog.articles adını taşır.

Bir servisi yeniden adlandırmamız gerekirse, geriye dönük uyumluluğu korumak için orijinal adla bir takma ad (alias) oluşturabiliriz. Nette bunu benzer şekilde yapar, örn. önceki adı router altında da mevcut olan routing.router servisi için.

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

Dosyadan Servis Yükleme

Servisleri yalnızca ContainerBuilder sınıfının API'sini kullanarak değil, aynı zamanda yapılandırma dosyasında services bölümünde kullanılan bilinen yazımla da oluşturabiliriz. @extension ön eki mevcut uzantıyı temsil eder.

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

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

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

Servisleri yükleriz:

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

		// uzantı için yapılandırma dosyasını yükleme
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}

beforeCompile()

Metot, konteynerin loadConfiguration metotlarında bireysel uzantılar tarafından eklenen tüm servisleri ve ayrıca kullanıcı yapılandırma dosyalarını içerdiği anda çağrılır. Bu derleme aşamasında, servis tanımlarını düzenleyebilir veya aralarındaki bağlantıları tamamlayabiliriz. Konteynerdeki servisleri etiketlere göre aramak için findByTag() metodunu, sınıfa veya arayüze göre aramak için ise findByType() metodunu kullanabiliriz.

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()

Bu aşamada, konteyner sınıfı zaten bir ClassType nesnesi şeklinde üretilmiştir, servisleri oluşturan tüm metotları içerir ve önbelleğe yazılmaya hazırdır. Sonuç kodunu bu noktada hala düzenleyebiliriz.

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

$initialization

Configurator sınıfı, konteyner oluşturulduktan sonra $this->initialization nesnesine addBody() metodu kullanılarak yazılan başlatma kodunu çağırır.

Örneğin, başlatma koduyla oturumu nasıl başlatacağımızı veya run etiketine sahip servisleri nasıl çalıştıracağımızı gösteren bir örnek:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// oturumun otomatik başlatılması
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// run etiketli servisler konteyner örneklendikten sonra oluşturulmalıdır
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}