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:
- getConfigSchema()
- loadConfiguration()
- beforeCompile()
- 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]);
}
}
}