Nette Documentation Preview

syntax
Üretilen Factory'ler
********************

.[perex]
Nette DI, arayüzlere dayanarak factory kodunu otomatik üretebilir ve sizi kod yazmaktan kurtarır.

Factory, nesneleri oluşturmaktan ve bağımlılıklarını aktarmaktan sorumlu bir sınıftır. Lütfen bunu, factory kullanımının belirli bir biçimini anlatan ve bu konuyla ilgisi olmayan *factory method* tasarım deseniyle karıştırmayın.

Böyle bir factory'nin nasıl göründüğünü [giriş bölümünde |introduction#Factory] gösterdik:

```php
class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}
```

Nette DI factory kodunu otomatik üretebilir. Tek yapmanız gereken bir arayüz oluşturmaktır; gerçekleştirimi Nette DI üretir. Arayüzde `create` adında tam olarak bir metot bulunmalı ve bir dönüş türü bildirilmelidir:

```php
interface ArticleFactory
{
	function create(): Article;
}
```

Yani `ArticleFactory` factory'sinin, `Article` nesneleri oluşturan bir `create` metodu vardır. `Article` sınıfı örneğin şöyle görünebilir:

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}
```

Factory'yi yapılandırma dosyasına ekleyin:

```neon
services:
	- ArticleFactory
```

Nette DI ilgili factory gerçekleştirimini üretir.

Factory'yi kullanan kodda nesneyi arayüzü üzerinden isteyin; Nette DI üretilen gerçekleştirimi verir:

```php
class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// bırakın nesneyi factory oluştursun
		$article = $this->articleFactory->create();
	}
}
```


Parametreli Factory
===================

`create` factory metodu, sonra yapıcıya aktardığı parametreler alabilir. Örneğin `Article` sınıfına makale yazarının ID'sini ekleyelim:

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}
```

Parametreyi factory'ye de ekleyeceğiz:

```php
interface ArticleFactory
{
	function create(int $authorId): Article;
}
```

Yapıcıdaki parametre adı (`$authorId`) factory metodundaki parametre adıyla eşleştiğinden, Nette DI onu otomatik olarak aktarır.


Gelişmiş Tanım
==============

Tanım, `implement` anahtarı kullanılarak çok satırlı biçimde de yazılabilir:

```neon
services:
	articleFactory:
		implement: ArticleFactory
```

Bu uzun biçimi kullanmak, sıradan servis tanımlarında olduğu gibi `arguments` anahtarıyla yapıcı için ek argümanlar belirtmeyi ve `setup` ile daha fazla yapılandırma yapmayı sağlar.

Örnek: `create()` metodu `$authorId` parametresini almasaydı, `Article` yapıcısına aktarılacak sabit bir değeri yapılandırmada verebilirdik:

```neon
services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123
```

Tersine, `create()` metodu `$authorId` parametresini alsaydı ama bu parametre yapıcının bir parçası olmayıp `Article::setAuthorId()` gibi bir metotla aktarılsaydı, parametreye `setup` bölümünde başvururduk:

```neon
services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)
```


Accessor
========

Nette, factory'lerin yanı sıra accessor denen nesneleri de üretebilir. Bunlar, DI container'dan belirli bir servisi döndüren bir `get()` metoduna sahip nesnelerdir. `get()` metodunun yinelenen çağrıları her zaman aynı örneği döndürür.

Accessor'lar, bağımlılıklar için tembel yükleme sağlar. Hataları özel bir veritabanına günlükleyen bir sınıf düşünün. Bu sınıf veritabanı bağlantısını yapıcı bağımlılık enjeksiyonuyla alsaydı, hatalar ender oluşsa ve bağlantı çoğu zaman kullanılmasa bile bağlantı her zaman kurulurdu. Bunun yerine sınıf bir accessor alabilir. Veritabanı nesnesi (bağlantı) yalnızca accessor'ın `get()` metodu ilk kez çağrıldığında oluşturulur.

Accessor nasıl oluşturulur? Yalnızca bir arayüz yazın; gerçekleştirimi Nette DI üretir. Arayüzde `get` adında, parametre almayan ve dönüş türü bildiren tam olarak bir metot bulunmalıdır:

```php
interface PDOAccessor
{
	function get(): PDO;
}
```

Accessor'ı, döndürmesi gereken servisin tanımıyla birlikte yapılandırma dosyasına ekleyin:

```neon
services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)
```

Accessor bir `PDO` servisi döndürdüğünden ve yapılandırmada bu türden yalnızca bir servis tanımlı olduğundan, accessor o servisi döndürür. Bu türden birden çok servis varsa, accessor'ın hangisini döndüreceğini adıyla belirtin, örneğin `- PDOAccessor(@db1)`.


Çoklu Factory/Accessor
======================

Şimdiye kadar factory'lerimiz ve accessor'larımız yalnızca tek bir nesne türünü oluşturabiliyor ya da döndürebiliyordu. Ancak factory ve accessor özelliklerini birleştiren çoklu factory'ler de kolayca oluşturabilirsiniz. Böyle bir bileşenin arayüzü `create<Name>()` ve `get<Name>()` adlı birden çok metot içerebilir, örneğin:

```php
interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}
```

Yani birden çok ayrı factory ve accessor enjekte etmek yerine tek ve daha kapsamlı bir bileşen enjekte edebilirsiniz.

Alternatif olarak, birden çok metot yerine parametreli bir `get()` kullanılabilir:

```php
interface MultiFactoryAlt
{
	function get($name): PDO;
}
```

O zaman `MultiFactory::getDb()`, `MultiFactoryAlt::get('db')` ile aynı işi yapar. Ancak bu alternatif yazımın sakıncası, `$name` için desteklenen değerlerin arayüz imzasından açıkça anlaşılmamasıdır. Ayrıca arayüzde farklı `$name` değerleri için farklı dönüş türleri tanımlayamazsınız.

Arayüz, `get($name)` yerine her çağrıda yeni bir örnek döndüren `create($name)` metodunu da bildirebilir (oysa `get()` paylaşılan bir örnek döndürür). Arayüzde böyle parametreli yalnızca bir metot bulunabilir. Metodun dönüş türü nullable ise (örneğin `?PDO`), bilinmeyen bir `$name` değeri için istisna fırlatmak yerine `null` döndürür.


Listeyle Tanım
--------------
Çoklu factory'yi yapılandırmada bir liste kullanarak, servisleri satır içinde yazarak tanımlayabilirsiniz: .{data-version:3.2.0}

```neon
services:
	- MultiFactory(
		article: Article()                    # createArticle() tanımlar
		db: PDO(%dsn%, %user%, %password%)    # getDb() tanımlar
	)
```

Alternatif olarak, çoklu factory tanımında var olan servislere referanslarla başvurabilirsiniz:

```neon
services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # createArticle() tanımlar
		db: @\PDO            # getDb() tanımlar
	)
```


Etiketlerle Tanım
-----------------

Çoklu factory tanımlamanın bir başka yolu da [etiketleri |services#Etiketler] kullanmaktır. Etiketin değeri, ilgili metodun adını belirler:

```neon
services:
	article:
		create: Article
		tags: {multi: article}     # createArticle() tanımlar
	db:
		create: PDO(%dsn%, %user%, %password%)
		tags: {multi: db}          # getDb() tanımlar

	- MultiFactory(tagged: multi)
```

Üretilen Factory'ler

Nette DI, arayüzlere dayanarak factory kodunu otomatik üretebilir ve sizi kod yazmaktan kurtarır.

Factory, nesneleri oluşturmaktan ve bağımlılıklarını aktarmaktan sorumlu bir sınıftır. Lütfen bunu, factory kullanımının belirli bir biçimini anlatan ve bu konuyla ilgisi olmayan factory method tasarım deseniyle karıştırmayın.

Böyle bir factory'nin nasıl göründüğünü giriş bölümünde gösterdik:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

Nette DI factory kodunu otomatik üretebilir. Tek yapmanız gereken bir arayüz oluşturmaktır; gerçekleştirimi Nette DI üretir. Arayüzde create adında tam olarak bir metot bulunmalı ve bir dönüş türü bildirilmelidir:

interface ArticleFactory
{
	function create(): Article;
}

Yani ArticleFactory factory'sinin, Article nesneleri oluşturan bir create metodu vardır. Article sınıfı örneğin şöyle görünebilir:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

Factory'yi yapılandırma dosyasına ekleyin:

services:
	- ArticleFactory

Nette DI ilgili factory gerçekleştirimini üretir.

Factory'yi kullanan kodda nesneyi arayüzü üzerinden isteyin; Nette DI üretilen gerçekleştirimi verir:

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// bırakın nesneyi factory oluştursun
		$article = $this->articleFactory->create();
	}
}

Parametreli Factory

create factory metodu, sonra yapıcıya aktardığı parametreler alabilir. Örneğin Article sınıfına makale yazarının ID'sini ekleyelim:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

Parametreyi factory'ye de ekleyeceğiz:

interface ArticleFactory
{
	function create(int $authorId): Article;
}

Yapıcıdaki parametre adı ($authorId) factory metodundaki parametre adıyla eşleştiğinden, Nette DI onu otomatik olarak aktarır.

Gelişmiş Tanım

Tanım, implement anahtarı kullanılarak çok satırlı biçimde de yazılabilir:

services:
	articleFactory:
		implement: ArticleFactory

Bu uzun biçimi kullanmak, sıradan servis tanımlarında olduğu gibi arguments anahtarıyla yapıcı için ek argümanlar belirtmeyi ve setup ile daha fazla yapılandırma yapmayı sağlar.

Örnek: create() metodu $authorId parametresini almasaydı, Article yapıcısına aktarılacak sabit bir değeri yapılandırmada verebilirdik:

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

Tersine, create() metodu $authorId parametresini alsaydı ama bu parametre yapıcının bir parçası olmayıp Article::setAuthorId() gibi bir metotla aktarılsaydı, parametreye setup bölümünde başvururduk:

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

Accessor

Nette, factory'lerin yanı sıra accessor denen nesneleri de üretebilir. Bunlar, DI container'dan belirli bir servisi döndüren bir get() metoduna sahip nesnelerdir. get() metodunun yinelenen çağrıları her zaman aynı örneği döndürür.

Accessor'lar, bağımlılıklar için tembel yükleme sağlar. Hataları özel bir veritabanına günlükleyen bir sınıf düşünün. Bu sınıf veritabanı bağlantısını yapıcı bağımlılık enjeksiyonuyla alsaydı, hatalar ender oluşsa ve bağlantı çoğu zaman kullanılmasa bile bağlantı her zaman kurulurdu. Bunun yerine sınıf bir accessor alabilir. Veritabanı nesnesi (bağlantı) yalnızca accessor'ın get() metodu ilk kez çağrıldığında oluşturulur.

Accessor nasıl oluşturulur? Yalnızca bir arayüz yazın; gerçekleştirimi Nette DI üretir. Arayüzde get adında, parametre almayan ve dönüş türü bildiren tam olarak bir metot bulunmalıdır:

interface PDOAccessor
{
	function get(): PDO;
}

Accessor'ı, döndürmesi gereken servisin tanımıyla birlikte yapılandırma dosyasına ekleyin:

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

Accessor bir PDO servisi döndürdüğünden ve yapılandırmada bu türden yalnızca bir servis tanımlı olduğundan, accessor o servisi döndürür. Bu türden birden çok servis varsa, accessor'ın hangisini döndüreceğini adıyla belirtin, örneğin - PDOAccessor(@db1).

Çoklu Factory/Accessor

Şimdiye kadar factory'lerimiz ve accessor'larımız yalnızca tek bir nesne türünü oluşturabiliyor ya da döndürebiliyordu. Ancak factory ve accessor özelliklerini birleştiren çoklu factory'ler de kolayca oluşturabilirsiniz. Böyle bir bileşenin arayüzü create<Name>() ve get<Name>() adlı birden çok metot içerebilir, örneğin:

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

Yani birden çok ayrı factory ve accessor enjekte etmek yerine tek ve daha kapsamlı bir bileşen enjekte edebilirsiniz.

Alternatif olarak, birden çok metot yerine parametreli bir get() kullanılabilir:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

O zaman MultiFactory::getDb(), MultiFactoryAlt::get('db') ile aynı işi yapar. Ancak bu alternatif yazımın sakıncası, $name için desteklenen değerlerin arayüz imzasından açıkça anlaşılmamasıdır. Ayrıca arayüzde farklı $name değerleri için farklı dönüş türleri tanımlayamazsınız.

Arayüz, get($name) yerine her çağrıda yeni bir örnek döndüren create($name) metodunu da bildirebilir (oysa get() paylaşılan bir örnek döndürür). Arayüzde böyle parametreli yalnızca bir metot bulunabilir. Metodun dönüş türü nullable ise (örneğin ?PDO), bilinmeyen bir $name değeri için istisna fırlatmak yerine null döndürür.

Listeyle Tanım

Çoklu factory'yi yapılandırmada bir liste kullanarak, servisleri satır içinde yazarak tanımlayabilirsiniz:

services:
	- MultiFactory(
		article: Article()                    # createArticle() tanımlar
		db: PDO(%dsn%, %user%, %password%)    # getDb() tanımlar
	)

Alternatif olarak, çoklu factory tanımında var olan servislere referanslarla başvurabilirsiniz:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # createArticle() tanımlar
		db: @\PDO            # getDb() tanımlar
	)

Etiketlerle Tanım

Çoklu factory tanımlamanın bir başka yolu da etiketleri kullanmaktır. Etiketin değeri, ilgili metodun adını belirler:

services:
	article:
		create: Article
		tags: {multi: article}     # createArticle() tanımlar
	db:
		create: PDO(%dsn%, %user%, %password%)
		tags: {multi: db}          # getDb() tanımlar

	- MultiFactory(tagged: multi)