Servislerin Tanımlanması

Yapılandırma, DI konteynerine bireysel servisleri nasıl oluşturacağını ve diğer bağımlılıklarla nasıl bağlayacağını öğrettiğimiz yerdir. Nette, bunu başarmak için çok net ve zarif bir yol sunar.

NEON formatındaki yapılandırma dosyasındaki services bölümü, kendi servislerimizi ve yapılandırmalarını tanımladığımız yerdir. PDO sınıfının bir örneğini temsil eden database adlı bir servisin basit bir tanım örneğine bakalım:

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

Yukarıdaki yapılandırma, DI konteynerinde aşağıdaki fabrika metoduna yol açacaktır:

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

Servis adları, yapılandırma dosyasının diğer bölümlerinde @servisAdi formatında onlara başvurmamızı sağlar. Bir servisi adlandırmaya gerek yoksa, basitçe bir tire kullanabiliriz:

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

DI konteynerinden bir servis almak için, parametre olarak servis adıyla getService() metodunu veya servis türüyle getByType() metodunu kullanabiliriz:

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

Servis Oluşturma

Çoğunlukla, bir servisi basitçe belirli bir sınıfın bir örneğini oluşturarak oluştururuz. Örneğin:

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

Yapılandırmayı ek anahtarlarla genişletmemiz gerekirse, tanımı birden çok satıra ayırabiliriz:

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

create anahtarının factory takma adı vardır, her iki varyant da pratikte yaygındır. Ancak, create kullanmanızı öneririz.

Yapıcı veya oluşturma metodunun argümanları alternatif olarak arguments anahtarında yazılabilir:

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

Servisler sadece bir sınıfın örneğini basitçe oluşturarak oluşturulmak zorunda değildir, aynı zamanda statik metotların veya diğer servislerin metotlarının çağrılmasının sonucu da olabilirler:

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

Basitlik için -> yerine :: kullanıldığına dikkat edin, bkz. #İfade Araçları. Bu fabrika metotları üretilecektir:

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

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

DI konteynerinin oluşturulan servisin türünü bilmesi gerekir. Belirtilen bir dönüş türü olmayan bir metot kullanarak bir servis oluşturuyorsak, bu türü yapılandırmada açıkça belirtmemiz gerekir:

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

Argümanlar

Yapıcıya ve metotlara argümanları, PHP'nin kendisinde olduğu gibi çok benzer bir şekilde iletiyoruz:

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

Daha iyi okunabilirlik için argümanları ayrı satırlara ayırabiliriz. Bu durumda virgül kullanımı isteğe bağlıdır:

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

Argümanları adlandırabilir ve sıralarıyla ilgilenmek zorunda kalmazsınız:

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

Bazı argümanları atlamak ve varsayılan değerlerini kullanmak veya autowiring kullanarak bir servis eklemek isterseniz, alt çizgi kullanın:

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

Argüman olarak servisleri iletebilir, parametreleri kullanabilir ve çok daha fazlasını yapabilirsiniz, bkz. #İfade Araçları.

Setup

setup bölümünde, servis oluşturulurken çağrılması gereken metotları tanımlarız.

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

Bu, PHP'de şöyle görünürdü:

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

Metotları çağırmanın yanı sıra, özelliklere değerler de iletebilirsiniz. Bir diziye öğe eklemek de desteklenir, bu, NEON sözdizimiyle çakışmaması için tırnak içinde yazılmalıdır:

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

Bu, PHP kodunda şöyle görünürdü:

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

Ancak setup'ta statik metotları veya diğer servislerin metotlarını da çağırabilirsiniz. Argüman olarak mevcut servisi iletmeniz gerekiyorsa, onu @self olarak belirtin:

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

Basitlik için -> yerine :: kullanıldığına dikkat edin, bkz. #İfade Araçları. Böyle bir fabrika metodu üretilecektir:

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

İfade Araçları

Nette DI, neredeyse her şeyi yazabileceğimiz son derece zengin ifade araçları sunar. Yapılandırma dosyalarında parametreler kullanabiliriz:

# parametre
%wwwDir%

# anahtar altındaki parametre değeri
%mailer.user%

# dize içindeki parametre
'%wwwDir%/images'

Ayrıca nesneler oluşturabilir, metotları ve fonksiyonları çağırabiliriz:

# nesne oluşturma
DateTime()

# statik metot çağırma
Collator::create(%locale%)

# PHP fonksiyonu çağırma
::getenv(DB_USER)

Servislere adlarıyla veya türleriyle başvurabiliriz:

# ada göre servis
@database

# türe göre servis
@Nette\Database\Connection

Birinci sınıf çağrılabilir sözdizimini kullanın:

# geri arama oluşturma, [@user, logout] benzeri
@user::logout(...)

Sabitleri kullanın:

# sınıf sabiti
FilesystemIterator::SKIP_DOTS

# global sabiti PHP fonksiyonu constant() ile alırız
::constant(PHP_VERSION)

Metot çağrıları PHP'de olduğu gibi zincirlenebilir. Sadece basitlik için -> yerine :: kullanılır:

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

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

Bu ifadeleri her yerde, servis oluşturma, argümanlarda, #Setup bölümünde veya parametrelerde kullanabilirsiniz:

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

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

Özel Fonksiyonlar

Yapılandırma dosyalarında şu özel fonksiyonları kullanabilirsiniz:

  • not() değerin olumsuzlanması
  • bool(), int(), float(), string() kayıpsız olarak belirtilen türe dönüştürme
  • typed() belirtilen türdeki tüm servislerin bir dizisini oluşturur
  • tagged() belirtilen etikete sahip tüm servislerin bir dizisini oluşturur
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

PHP'deki klasik tür dönüştürme, örneğin (int) gibi, aksine kayıpsız tür dönüştürme sayısal olmayan değerler için bir istisna fırlatır.

typed() fonksiyonu, belirtilen türdeki (sınıf veya arayüz) tüm servislerin bir dizisini oluşturur. Autowiring'i devre dışı bırakılmış servisleri atlar. Virgülle ayrılmış birden çok tür de belirtebilirsiniz.

services:
	- BarsDependent( typed(Bar) )

Belirli bir türdeki servislerin dizisini autowiring kullanarak otomatik olarak argüman olarak da iletebilirsiniz.

tagged() fonksiyonu daha sonra belirli bir etikete sahip tüm servislerin bir dizisini oluşturur. Burada da virgülle ayrılmış birden çok etiket belirtebilirsiniz.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

autowired anahtarı, belirli bir servis için autowiring davranışını etkilemenizi sağlar. Ayrıntılar için autowiring bölümü bakın.

services:
	foo:
		create: Foo
		autowired: false     # foo servisi autowiring'den çıkarıldı

Lazy Servisler

Lazy loading, bir servisin oluşturulmasını gerçekten ihtiyaç duyulana kadar erteleyen bir tekniktir. Global yapılandırmada, tüm servisler için aynı anda lazy oluşturmayı etkinleştirin. Bireysel servisler için daha sonra bu davranışı geçersiz kılabilirsiniz:

services:
	foo:
		create: Foo
		lazy: false

Bir servis lazy olarak tanımlandığında, DI konteynerinden istendiğinde özel bir yer tutucu nesne alırız. Bu, gerçek servis gibi görünür ve davranır, ancak gerçek başlatma (yapıcı ve setup çağrısı) yalnızca herhangi bir metodunun veya özelliğinin ilk çağrısında gerçekleşir.

Lazy loading yalnızca kullanıcı sınıfları için kullanılabilir, dahili PHP sınıfları için kullanılamaz. PHP 8.4 veya daha yenisini gerektirir.

Etiketler (Tags)

Etiketler, servislere ek bilgiler eklemek için kullanılır. Bir servise bir veya daha fazla etiket ekleyebilirsiniz:

services:
	foo:
		create: Foo
		tags:
			- cached

Etiketler ayrıca değerler de taşıyabilir:

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

Belirli etiketlere sahip tüm servisleri almak için tagged() fonksiyonunu kullanabilirsiniz:

services:
	- LoggersDependent( tagged(logger) )

DI konteynerinde, belirli bir etikete sahip tüm servislerin adlarını findByTag() metodunu kullanarak alabilirsiniz:

$names = $container->findByTag('logger');
// $names, servis adını ve etiket değerini içeren bir dizidir
// örn. ['foo' => 'monolog.logger.event', ...]

Inject Modu

inject: true bayrağı kullanılarak, inject anotasyonuna sahip public değişkenler ve inject*() metotları aracılığıyla bağımlılıkların iletilmesi etkinleştirilir.

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

Varsayılan olarak, inject yalnızca presenter'lar için etkinleştirilir.

Servislerin Değiştirilmesi

DI konteyneri, yerleşik veya kullanıcı uzantısı aracılığıyla eklenen birçok servis içerir. Bu servislerin tanımlarını doğrudan yapılandırmada değiştirebilirsiniz. Örneğin, application.application servisinin sınıfını, standart olarak Nette\Application\Application olan, başka bir sınıfla değiştirebilirsiniz:

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

alteration bayrağı bilgilendiricidir ve yalnızca mevcut bir servisi değiştirdiğimizi belirtir.

Setup'ı da tamamlayabiliriz:

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

Bir servisi yeniden yazarken, orijinal argümanları, setup öğelerini veya etiketleri kaldırmak isteyebiliriz, bunun için reset kullanılır:

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

Bir uzantı tarafından eklenen bir servisi kaldırmak isterseniz, bunu şu şekilde yapabilirsiniz:

services:
	cache.journal: false
versiyon: 3.x