Takip et

Drupal Service Decorators: Mevcut Yapıyı Bozmadan Değiştirmenin Sanatı

Drupal, güçlü ve esnek bir içerik yönetim sistemi olmasının yanı sıra, modern PHP geliştirme prensiplerini benimseyen bir framework’tür. Bu prensi…

Drupal Service Decorators: Mevcut Yapıyı Bozmadan Değiştirmenin Sanatı

Drupal, güçlü ve esnek bir içerik yönetim sistemi olmasının yanı sıra, modern PHP geliştirme prensiplerini benimseyen bir framework’tür. Bu prensiplerden biri de servis tabanlı mimari ve bağımlılık enjeksiyonudur. Ancak bazen, Drupal çekirdeği veya katkıda bulunan modüller tarafından sağlanan mevcut bir servisin davranışını değiştirmemiz veya genişletmemiz gerekebilir. İşte tam bu noktada, “Service Decorator”lar devreye girer. Bu makale, Drupal Service Decorator’ların ne olduğunu, nasıl çalıştığını ve mevcut kodu bozmadan, temiz ve sürdürülebilir bir şekilde nasıl değişiklik yapabileceğinizi detaylı bir şekilde açıklayacaktır.

Giriş: Neden Service Decorator’lara İhtiyaç Duyarız?

Drupal projelerinde geliştirme yaparken sıkça karşılaşılan bir senaryo, mevcut bir servisin belirli bir yöntemini değiştirmek veya bu yönteme ek işlevsellik katmaktır. Doğrudan çekirdek veya katkıda bulunan modül kodunu değiştirmek (hacklemek) kesinlikle kaçınılması gereken bir uygulamadır, zira bu tür değişiklikler güncelleme süreçlerini kabusa çevirir ve projenin bakımını imkansız hale getirir. Service Decorator’lar, bu soruna zarif bir çözüm sunar.

Mevcut Hizmetleri Genişletme İhtiyacı

Birçok durumda, Drupal’ın varsayılan davranışları projenizin özel gereksinimlerini karşılamayabilir. Örneğin, kullanıcı oturumlarını farklı bir şekilde yönetmek, bir önbellekleme servisine özel bir mantık eklemek veya bir günlük kaydı servisine ek bilgiler yazdırmak isteyebilirsiniz. Bu tür senaryolarda, mevcut bir servisi genişletmek veya sarmalamak gerekir.

Kodu Korumak ve Bakım Kolaylığı

Service Decorator’lar, mevcut kod tabanını dokunulmaz bırakarak, değişikliklerinizi ayrı bir katmanda yapmanızı sağlar. Bu, çekirdek veya katkıda bulunan modül güncellemelerinin sorunsuz bir şekilde uygulanabileceği anlamına gelir. Ayrıca, kendi kodunuzun sorumluluklarını net bir şekilde ayırarak, bakım kolaylığını ve hata ayıklama süreçlerini önemli ölçüde iyileştirir.

Esneklik ve Modülerlik

Decorator deseni, uygulamanıza daha fazla esneklik ve modülerlik kazandırır. Bir servisin davranışını değiştirmek istediğinizde, mevcut servisi değiştirmek yerine, yeni bir “dekorasyon” katmanı eklersiniz. Bu, farklı projelerde veya farklı durumlarda aynı servisin farklı şekillerde dekore edilmesine olanak tanır.

Drupal’da Servisler ve Bağımlılık Enjeksiyonu

Service Decorator’ları anlamak için öncelikle Drupal’daki servis kavramını ve bağımlılık enjeksiyonunu iyi kavramak gerekir.

Servislerin Temelleri

Drupal 8 ve sonrası, Symfony çerçevesinin bileşenlerini yoğun bir şekilde kullanır. “Servis”, belirli bir işlevselliği yerine getiren bir PHP sınıfıdır. Bu sınıflar, genellikle tekil (singleton) olarak tanımlanır ve uygulamanın farklı yerlerinden erişilebilir. Örneğin, veritabanı işlemleri için bir servis, kullanıcı yönetimi için başka bir servis veya önbellekleme için ayrı bir servis bulunur.

Bağımlılık Enjeksiyonu ve Servis Konteyneri

Bağımlılık Enjeksiyonu (DI), bir sınıfın bağımlılıklarını (yani, ihtiyaç duyduğu diğer nesneleri) kendisinin oluşturması yerine, dışarıdan sağlanması prensibidir. Drupal’da bu, bir “Servis Konteyneri” (Symfony’nin ContainerBuilder‘ı) aracılığıyla yönetilir. Servis konteyneri, tüm servis tanımlarını bilir ve bir servise ihtiyaç duyulduğunda, gerekli bağımlılıkları otomatik olarak enjekte ederek servisin bir örneğini oluşturur.

Servis Tanımlamaları: services.yml

Drupal’da servisler, genellikle bir modülün veya temanın kök dizinindeki services.yml dosyasında tanımlanır. Bu YAML dosyası, servisin adını, sınıfını, bağımlılıklarını (argümanlarını) ve diğer yapılandırma seçeneklerini belirtir. İşte basit bir servis tanımı örneği:


services:
  mymodule.my_service:
    class: Drupal\mymodule\MyService
    arguments: ['@logger.factory', '@current_user']

Bu tanım, mymodule.my_service adında bir servis oluşturur ve bu servisin yapıcı metoduna logger.factory ve current_user servislerini enjekte eder.

Service Decorator Nedir ve Nasıl Çalışır?

Service Decorator, mevcut bir servisi sarmalayarak (decorate ederek) onun davranışını değiştiren veya genişleten özel bir servis türüdür. Bu, orijinal servisin kodunu değiştirmeden ek işlevsellik eklemenizi sağlar.

Decorator Tasarım Deseni

Decorator, nesne yönelimli programlamada iyi bilinen bir yapısal tasarım desenidir. Amacı, mevcut bir nesneye dinamik olarak yeni sorumluluklar eklemektir. Dekorasyon, alt sınıflara ayırma (inheritance) yoluyla genişletmeye esnek bir alternatif sunar. Bir dekoratör, sarmaladığı nesneyle aynı arayüze sahip olmalı ve orijinal nesnenin yöntemlerini çağırarak kendi ek mantığını uygulayabilir.

Drupal'daki Service Decorator Mekanizması

Drupal'da bir servisi dekore etmek için, yeni servisinizi services.yml dosyasında tanımlarken decorates anahtarını kullanırsınız. Bu anahtar, hangi servisi dekore ettiğinizi belirtir. Ayrıca, dekore edilen orijinal servisi yeni servisinize bağımlılık olarak enjekte etmek için özel bir argüman kullanmanız gerekir.


services:
  mymodule.my_decorated_service:
    class: Drupal\mymodule\MyDecoratedService
    decorates: original_service_id
    arguments: ['@mymodule.my_decorated_service.inner']

Burada original_service_id dekore edilen servisin kimliğidir. @mymodule.my_decorated_service.inner ise Drupal'ın otomatik olarak oluşturduğu, orijinal servisin örneğini temsil eden özel bir referanstır. Bu referans, dekore eden servisin yapıcı metoduna orijinal servisi enjekte etmesini sağlar.

Decorator'ın Avantajları ve Dezavantajları

Avantajları:

  • Non-invaziv: Çekirdek veya katkıda bulunan modül kodunu değiştirmenize gerek kalmaz.
  • Sürdürülebilirlik: Güncellemelerden etkilenmez, projenizin bakımını kolaylaştırır.
  • Modülerlik: Farklı dekoratörler zincirleyerek karmaşık davranışlar oluşturabilirsiniz.
  • Sorumluluk Ayrımı: Her dekoratörün belirli bir sorumluluğu vardır, bu da kodun okunabilirliğini artırır.

Dezavantajları:

  • Karmaşıklık: Aşırı kullanıldığında, bir servisin davranışını izlemek zorlaşabilir.
  • Hata Ayıklama: Bir servisin birden fazla dekoratör tarafından sarmalanması, hata ayıklamayı biraz daha karmaşık hale getirebilir.
  • Performans: Her dekorasyon katmanı, çağrı yığınına ek bir adım ekler, ancak genellikle ihmal edilebilir bir performans maliyeti vardır.

Bir Service Decorator Oluşturma Adımları

Şimdi pratik bir örnek üzerinden bir Service Decorator'ın nasıl oluşturulacağını adım adım inceleyelim.

Hedef Servisi Belirleme

İlk adım, hangi servisi dekore etmek istediğinizi belirlemektir. Bu örnekte, Drupal'ın varsayılan günlük kaydı servisi olan logger.factory servisini dekore edeceğiz. Amacımız, her günlük kaydı işlemine özel bir ön ek eklemek olsun.

Decorator Servis Tanımlaması (services.yml)

Modülünüzün (örneğin, mymodule) mymodule.services.yml dosyasına aşağıdaki tanımı ekleyin:


# mymodule/mymodule.services.yml
services:
  mymodule.logger_factory_decorator:
    class: Drupal\mymodule\Logger\MyModuleLoggerFactoryDecorator
    decorates: logger.factory
    public: false # Bu servisin doğrudan çağrılmasını engeller
    arguments: ['@mymodule.logger_factory_decorator.inner']

Burada:

  • mymodule.logger_factory_decorator: Yeni dekoratör servisimizin benzersiz kimliği.
  • class: Dekorasyon mantığını içeren PHP sınıfımızın yolu.
  • decorates: logger.factory: Hangi servisi dekore ettiğimizi belirtiriz. Bu, Drupal'a logger.factory servisi çağrıldığında aslında bizim dekoratörümüzün çağrılacağını söyler.
  • public: false: Genellikle dekoratör servisleri doğrudan kullanılmadığı için public: false olarak ayarlanır. Bu, servisin sadece iç kullanım için olduğunu belirtir.
  • arguments: ['@mymodule.logger_factory_decorator.inner']: Bu özel argüman, orijinal logger.factory servisini dekoratör sınıfımızın yapıcı metoduna enjekte eder.

Decorator Sınıfını Yazma

Şimdi Drupal\mymodule\Logger\MyModuleLoggerFactoryDecorator sınıfını oluşturalım. Bu sınıf, orijinal LoggerFactory sınıfının arayüzünü (\Psr\Log\LoggerFactoryInterface) uygulamalıdır.


// mymodule/src/Logger/MyModuleLoggerFactoryDecorator.php
namespace Drupal\mymodule\Logger;

use Psr\Log\LoggerInterface;
use Psr\Log\LoggerFactoryInterface;

/**
 * Decorates the core logger.factory service to add custom prefixes.
 */
class MyModuleLoggerFactoryDecorator implements LoggerFactoryInterface {

  /**
   * The decorated logger factory.
   *
   * @var \Psr\Log\LoggerFactoryInterface
   */
  protected $inner;

  /**
   * Constructs a MyModuleLoggerFactoryDecorator object.
   *
   * @param \Psr\Log\LoggerFactoryInterface $inner
   *   The decorated logger factory service.
   */
  public function __construct(LoggerFactoryInterface $inner) {
    $this->inner = $inner;
  }

  /**
   * {@inheritdoc}
   */
  public function get(string $channel): LoggerInterface {
    // Get the original logger for the given channel.
    $original_logger = $this->inner->get($channel);

    // Now, we can wrap this logger with our own custom logger that adds prefixes.
    // For simplicity, let's assume we return a custom logger instance.
    // In a real-world scenario, you might have a custom Logger class
    // that wraps the original one and modifies its log() method.
    // For this example, we'll just demonstrate the decoration principle.

    // A more realistic approach would be to decorate the LoggerInterface itself,
    // not just the factory. But for demonstrating LoggerFactory decoration:
    return new MyModuleLoggerDecorator($original_logger, 'MY_PREFIX');
  }

}

Yukarıdaki örnekte, MyModuleLoggerFactoryDecorator sınıfı, LoggerFactoryInterface arayüzünü uygular ve yapıcı metodunda orijinal logger.factory servisini alır. get() metodunu çağrıldığında, orijinal factory'den bir logger alır ve onu kendi özel logger'ımız olan MyModuleLoggerDecorator ile sarmalar. Şimdi MyModuleLoggerDecorator sınıfını da oluşturalım:


// mymodule/src/Logger/MyModuleLoggerDecorator.php
namespace Drupal\mymodule\Logger;

use Psr\Log\AbstractLogger;
use Psr\Log\LoggerInterface;

/**
 * Decorates a logger to add a custom prefix to log messages.
 */
class MyModuleLoggerDecorator extends AbstractLogger {

  /**
   * The decorated logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $innerLogger;

  /**
   * The custom prefix to add to log messages.
   *
   * @var string
   */
  protected $prefix;

  /**
   * Constructs a MyModuleLoggerDecorator object.
   *
   * @param \Psr\Log\LoggerInterface $innerLogger
   *   The decorated logger service.
   * @param string $prefix
   *   The prefix to add to log messages.
   */
  public function __construct(LoggerInterface $innerLogger, string $prefix) {
    $this->innerLogger = $innerLogger;
    $this->prefix = $prefix;
  }

  /**
   * {@inheritdoc}
   */
  public function log($level, $message, array $context = []): void {
    // Add our custom prefix to the message.
    $prefixed_message = '[' . $this->prefix . '] ' . $message;

    // Call the original logger's log method with the modified message.
    $this->innerLogger->log($level, $prefixed_message, $context);
  }

}

Bu örnekte, MyModuleLoggerDecorator sınıfı, AbstractLogger'ı genişleterek LoggerInterface'in tüm metodlarını devralır ve sadece log() metodunu geçersiz kılar. Bu metod, orijinal mesajın başına bir ön ek ekler ve ardından orijinal logger'ın log() metodunu çağırır. Bu sayede, logger.factory servisini kullanan her yerde, günlük mesajlarımızın başında artık özel ön ekimiz olacaktır.

Gelişmiş Kullanım Senaryoları ve İpuçları

Service Decorator'lar sadece basit değişiklikler için değil, daha karmaşık senaryolar için de kullanılabilir.

Birden Fazla Decorator Zincirleme

Bir servisi birden fazla dekoratör ile sarmalayabilirsiniz. Drupal, bu zinciri services.yml dosyasındaki decorates tanımlarına göre otomatik olarak yönetir. Örneğin, logger.factory servisini önce bir dekoratör, sonra başka bir dekoratör ile sarmalayabilirsiniz. Drupal, en son dekore eden servisi çağırır ve bu servis, bir önceki dekoratörü çağırır ve bu böyle devam eder. Bu, her bir dekoratörün kendi özel mantığını eklemesine olanak tanır.


services:
  mymodule.logger_factory_decorator_one:
    class: Drupal\mymodule\Logger\DecoratorOne
    decorates: logger.factory
    public: false
    arguments: ['@mymodule.logger_factory_decorator_one.inner']

  mymodule.logger_factory_decorator_two:
    class: Drupal\mymodule\Logger\DecoratorTwo
    decorates: logger.factory
    public: false
    arguments: ['@mymodule.logger_factory_decorator_two.inner']
    # priority: 100 # İsteğe bağlı, varsayılan 0'dır. Daha yüksek öncelik, daha önce dekore eder.

Bu durumda, DecoratorTwo, DecoratorOne'ı sarmalar ve DecoratorOne da orijinal logger.factory'yi sarmalar.

Decorator Öncelikleri (priority)

Eğer birden fazla dekoratör aynı servisi dekore ediyorsa ve bunların uygulanma sırası önemliyse, priority anahtarını kullanabilirsiniz. Daha yüksek priority değerine sahip dekoratörler, daha düşük öncelikli dekoratörlerden önce uygulanır (yani, en dıştaki sarmalayıcı olurlar). Varsayılan öncelik 0'dır.


services:
  mymodule.logger_factory_decorator_one:
    class: Drupal\mymodule\Logger\DecoratorOne
    decorates: logger.factory
    public: false
    arguments: ['@mymodule.logger_factory_decorator_one.inner']
    priority: 100 # Bu, diğerlerinden önce uygulanır.

  mymodule.logger_factory_decorator_two:
    class: Drupal\mymodule\Logger\DecoratorTwo
    decorates: logger.factory
    public: false
    arguments: ['@mymodule.logger_factory_decorator_two.inner']
    priority: 50 # Bu, DecoratorOne'dan sonra uygulanır.

Hata Ayıklama ve Sorun Giderme

Bir servisin dekore edilip edilmediğini veya hangi dekoratörler tarafından sarmalandığını anlamak için drush debug:container komutunu kullanabilirsiniz:


drush debug:container logger.factory

Bu komut, servisin tanımını ve varsa hangi dekoratörler tarafından sarmalandığını gösterecektir. Bu, özellikle karmaşık servis zincirlerinde hata ayıklama yaparken çok faydalıdır.

Ne Zaman Decorator Kullanmalı, Ne Zaman Farklı Bir Yaklaşım?

Service Decorator'lar güçlü olsa da, her senaryo için en iyi çözüm değildir. İşte bazı alternatifler:

  • Event Subscriber (Olay Dinleyicisi): Eğer bir olay (örneğin, bir varlık kaydedildiğinde) gerçekleştiğinde ek bir işlem yapmak istiyorsanız, Event Subscriber'lar daha uygun olabilir. Decorator'lar bir servisin metod çağrılarını doğrudan değiştirmek veya sarmalamak için kullanılırken, Event Subscriber'lar daha çok "bir şey olduğunda tepki ver" senaryoları içindir.
  • Plugin'ler: Drupal'da belirli bir türde (örneğin, bloklar, alan biçimlendiriciler) yeni bir işlevsellik eklemek istediğinizde Plugin'ler kullanılır. Plugin'ler, belirli bir arayüzü uygulayan küçük, takılabilir bileşenlerdir.
  • Service Provider: Eğer bir servisin tanımını veya argümanlarını dinamik olarak değiştirmek istiyorsanız, bir Service Provider kullanabilirsiniz. Bu, genellikle daha düşük seviyeli bir kontrol sağlar.

Kısacası, bir servisin mevcut metodlarının davranışını değiştirmek veya genişletmek istediğinizde, Decorator'lar genellikle en temiz ve en sürdürülebilir yoldur. Eğer sadece bir olaya tepki vermek veya yeni bir tür işlevsellik eklemek istiyorsanız, diğer yaklaşımları değerlendirmelisiniz.

Sonuç

Drupal Service Decorator'lar, mevcut çekirdek veya katkıda bulunan modül servislerinin davranışını güvenli, temiz ve sürdürülebilir bir şekilde değiştirmek veya genişletmek için güçlü bir mekanizma sunar. Bu desen, projenizin esnekliğini artırır, bakım maliyetlerini düşürür ve gelecekteki güncellemelerle uyumluluğu sağlar. services.yml dosyasındaki basit bir tanım ve uygun bir PHP sınıfı ile, Drupal'ın güçlü servis mimarisinden tam olarak yararlanabilirsiniz.

Özet ve Önemli Çıkarımlar

  • Service Decorator'lar, mevcut servisleri "hacklemek" yerine sarmalayarak değiştirmenin en iyi yoludur.
  • decorates anahtarı ve .inner argümanı, bir dekoratör tanımlamanın temelidir.
  • Decorator'lar, orijinal servisin arayüzünü uygulamalıdır.
  • Birden fazla dekoratörü zincirleyebilir ve priority ile sıralarını belirleyebilirsiniz.
  • drush debug:container, dekoratörleri hata ayıklamak için vazgeçilmez bir araçtır.

Bu yaklaşımı benimseyerek, Drupal projelerinizde daha temiz, daha modüler ve daha yönetilebilir bir kod tabanı oluşturabilirsiniz. Mevcut yapıyı bozmadan, l'art de modifier l'existant sans tout casser prensibini benimseyerek, Drupal geliştirme deneyiminizi bir üst seviyeye taşıyın.

Sıkça Sorulan Sorular (SSS)

Decorator kullanmak performans düşürür mü?

Her dekorasyon katmanı, bir metod çağrısına ek bir adım ekler. Teorik olarak bu, çok küçük bir performans maliyeti anlamına gelir. Ancak modern PHP yorumlayıcıları ve Drupal'ın önbellekleme mekanizmaları sayesinde, bu maliyet çoğu zaman ihmal edilebilir düzeydedir ve çoğu uygulama için fark edilmez. Aşırıya kaçmadığınız sürece performans endişesi taşımanıza gerek yoktur.

Hangi Drupal sürümlerinde Service Decorator'lar desteklenir?

Service Decorator'lar, Drupal 8'den itibaren tam olarak desteklenmektedir. Drupal 9 ve Drupal 10'da da aynı mekanizma geçerlidir.

Decorator yerine Event Subscriber veya Plugin kullanmak ne zaman daha iyi olur?

Decorator'lar, mevcut bir servisin belirli bir metodunun davranışını değiştirmek veya sarmalamak istediğinizde idealdir. Eğer bir olay (örneğin, bir varlık güncellendiğinde) gerçekleştiğinde tetiklenecek ek bir mantık eklemek istiyorsanız Event Subscriber kullanın. Eğer Drupal'ın belirli bir uzantı noktasına (örneğin, bir blok, bir alan biçimlendirici) yeni bir tür işlevsellik eklemek istiyorsanız Plugin'leri tercih edin.

Bir servisi dekore ettiğimi nasıl anlarım?

En kolay yol, drush debug:container [servis_adi] komutunu kullanmaktır. Bu komut, servisin tanımını ve varsa hangi dekoratörler tarafından sarmalandığını detaylı bir şekilde gösterir. Ayrıca, kodunuzda services.yml dosyalarını kontrol ederek decorates anahtarını arayabilirsiniz.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Mobil sürümden çık