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