Nette DI için Uzantılar Oluşturma

Yapılandırma dosyalarına ek olarak bir DI konteyneri oluşturmak, uzantılar olarak adlandırılanları da etkiler. Bunları extensions bölümündeki yapılandırma dosyasında etkinleştiriyoruz.

BlogExtension sınıfı tarafından temsil edilen uzantıyı blog adıyla bu şekilde ekleriz:

extensions:
	blog: BlogExtension

Her derleyici uzantısı Nette\DI\CompilerExtension adresinden miras alır ve DI derlemesi sırasında çağrılan aşağıdaki yöntemleri uygulayabilir:

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

getConfigSchema()

Bu yöntem ilk olarak çağrılır. Yapılandırma parametrelerini doğrulamak için kullanılan şemayı tanımlar.

Uzantılar, adı uzantının eklendiği bölümle aynı olan bir bölümde yapılandırılır, örneğin blog.

# uzantımla aynı isim
blog:
	postsPerPage: 10
	comments: false

Türleri, kabul edilen değerleri ve muhtemelen varsayılan değerleri de dahil olmak üzere tüm yapılandırma seçeneklerini tanımlayan bir şema tanımlayacağız:

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

Belgeler için Şema 'ya bakın. Ayrıca, dynamic() adresini kullanarak hangi seçeneklerin dinamik olabileceğini belirtebilirsiniz, örneğin Expect::int()->dynamic().

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

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

loadConfiguration()

Bu yöntem konteynere hizmet eklemek için kullanılır. Bu işlem Nette\DI\ContainerBuilder tarafından yapı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, bir uzantı tarafından eklenen hizmetlerin önüne kendi adını eklemektir, böylece isim çakışmaları ortaya çıkmaz. Bu prefix() tarafından yapılır, bu nedenle uzantı ‘blog’ olarak adlandırılırsa, hizmet blog.articles olarak adlandırılacaktır.

Bir hizmeti yeniden adlandırmamız gerekirse, geriye dönük uyumluluğu korumak için orijinal adıyla bir takma ad oluşturabiliriz. Benzer şekilde, Nette'in örneğin routing.router için yaptığı şey budur, bu da daha önceki router adı altında da mevcuttur.

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

Hizmetleri Bir Dosyadan Alma

ContainerBuilder API'sini kullanarak hizmetler oluşturabiliriz, ancak bunları tanıdık NEON yapılandırma dosyası ve services bölümü aracılığıyla da ekleyebiliriz. @extension öneki 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)

Hizmetleri bu şekilde ekleyeceğiz:

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

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

beforeCompile()

Kapsayıcı, loadConfiguration yöntemlerinde bireysel uzantılar tarafından eklenen tüm hizmetleri ve kullanıcı yapılandırma dosyalarını içerdiğinde yöntem çağrılır. Birleştirmenin bu aşamasında, daha sonra hizmet tanımlarını değiştirebilir veya aralarına bağlantılar ekleyebiliriz. Hizmetleri etiketlere göre aramak için findByTag() yöntemini veya sınıf ya da arayüze göre aramak için findByType() yöntemini kullanabilirsiniz.

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 olarak üretilmiştir, servisin oluşturduğu tüm yöntemleri içerir ve PHP dosyası olarak önbelleğe alınmaya hazırdır. Bu noktada ortaya çıkan sınıf kodunu hala düzenleyebiliriz.

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

$başlatma

Yapılandırıcı, addBody() yöntemi kullanılarak bir $this->initialization nesnesine yazılarak oluşturulan konteyner oluşturulduktan sonra başlatma kodunu çağırır.

Bir oturumun nasıl başlatılacağına veya başlatma kodunu kullanarak run etiketine sahip hizmetlerin nasıl başlatılacağına dair bir örnek göstereceğiz:

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

		// 'run' etiketine sahip hizmetler konteyner oluşturulduktan sonra oluşturulmalıdır
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}
versiyon: 3.x