paint-brush
Laravel Under The Hood - Çerçeve Nasıl Genişletilirile@oussamamater
1,145 okumalar
1,145 okumalar

Laravel Under The Hood - Çerçeve Nasıl Genişletilir

ile Oussama Mater6m2024/05/29
Read on Terminal Reader

Çok uzun; Okumak

Laravel, genellikle 'fake()' yardımcısıyla eriştiğimiz FakerPHP'yi sarar. Faker PHP, 'valid()' ve 'unique()' gibi değiştiricilerle birlikte gelir, ancak aynı anda yalnızca birini kullanabilirsiniz. Bu beni şunu düşündürdü: Peki ya kendi değiştiricimizi yaratmak istiyorsak? Örneğin, "uniqueAndValid()" veya başka bir değiştirici.
featured image - Laravel Under The Hood - Çerçeve Nasıl Genişletilir
Oussama Mater HackerNoon profile picture

Birkaç gün önce hatalı bir testi düzeltiyordum ve fabrikamda bazı benzersiz ve geçerli değerlere ihtiyacım olduğu ortaya çıktı. Laravel, genellikle fake() yardımcısıyla eriştiğimiz FakerPHP'yi sarar. FakerPHP, valid() ve unique() gibi değiştiricilerle birlikte gelir, ancak aynı anda yalnızca birini kullanabilirsiniz, dolayısıyla fake()->unique()->valid() işlevini yapamazsınız, ki tam da ihtiyacım olan şey buydu.


Bu beni şunu düşündürdü: Peki ya kendi değiştiricimizi yaratmak istiyorsak? Örneğin, uniqueAndValid() veya başka bir değiştirici. Çerçeveyi nasıl genişletebiliriz ?

Sesli düşünmek

Düşünce trenimi terk edeceğim.


Aşırı mühendislik gerektiren herhangi bir çözüme geçmeden önce her zaman daha basit bir seçeneğin olup olmadığını kontrol etmek ve neyle uğraştığımı anlamak isterim. Şimdi fake() yardımcısına bir göz atalım:

 function fake($locale = null) { if (app()->bound('config')) { $locale ??= app('config')->get('app.faker_locale'); } $locale ??= 'en_US'; $abstract = \Faker\Generator::class.':'.$locale; if (! app()->bound($abstract)) { app()->singleton($abstract, fn () => \Faker\Factory::create($locale)); } return app()->make($abstract); }

Kodu okuyarak Laravel'in konteynere bir singleton bağladığını görebiliriz. Ancak abstract'a bakarsak herhangi bir arayüz uygulamayan normal bir sınıftır ve nesne bir fabrika aracılığıyla yaratılmıştır. Bu işleri karmaşıklaştırır. Neden?


  1. Çünkü eğer bu bir arayüz olsaydı, temel \Faker\Generator sınıfını genişleten yeni bir sınıf oluşturabilir, bazı yeni özellikler ekleyebilir ve onu konteynere yeniden bağlayabilirdik. Ama bizim bu lüksümüz yok.


  2. İlgili bir fabrika var. Bu, bunun basit bir örnekleme olmadığı anlamına gelir; çalıştırılan bir mantık var. Bu durumda fabrika bazı sağlayıcılar ekler (PhoneNumber, Text, UserAgent, vb.). Yani, yeniden bağlamayı denesek bile, orijinal \Faker\Generator dosyasını döndürecek olan fabrikayı kullanmak zorunda kalacağız.


Çözümler 🤔? Biri şöyle düşünebilir: "1. maddede özetlendiği gibi yeni jeneratörü geri getiren kendi fabrikamızı kurmamızı engelleyen şey nedir?" Hiçbir şey, bunu yapabiliriz ama yapmayacağız! Bir çerçeveyi çeşitli nedenlerden dolayı kullanırız; bunlardan biri güncellemelerdir. FakerPHP'ye yeni bir sağlayıcı eklenirse veya büyük bir yükseltme yapılırsa ne olacak? Laravel kodu ayarlayacak ve herhangi bir değişiklik yapmayan kişiler hiçbir şeyi fark etmeyecektir. Ancak, dışarıda bırakılırız ve hatta kodumuz bozulabilir (büyük olasılıkla). Yani evet, o kadar ileri gitmek istemiyoruz.

Peki ne yapıyoruz?

Artık temel seçenekleri araştırdığımıza göre tasarım desenleri gibi daha gelişmiş seçenekleri düşünmeye başlayabiliriz. Tam bir uygulamaya ihtiyacımız yok, sadece sorunumuza aşina olan bir şeye ihtiyacımız var. Bu yüzden her zaman onları tanımanın iyi olduğunu söylüyorum. Bu durumda, eski özellikleri korurken yeni özellikler ekleyerek Generator sınıfını "dekore edebiliriz". Kulağa iyi geliyor? Bakalım nasıl!


Öncelikle FakerGenerator yeni bir sınıf oluşturalım:

 <?php namespace App\Support; use Closure; use Faker\Generator; use Illuminate\Support\Traits\ForwardsCalls; class FakerGenerator { use ForwardsCalls; public function __construct(private readonly Generator $generator) { } public function uniqueAndValid(Closure $validator = null): UniqueAndValidGenerator { return new UniqueAndValidGenerator($this->generator, $validator); } public function __call($method, $parameters): mixed { return $this->forwardCallTo($this->generator, $method, $parameters); } }

Bu bizim "dekoratörümüz" olacak (bir nevi). Temel Generator bir bağımlılık olmasını bekleyen ve yeni bir değiştirici olan uniqueAndValid() tanıtan basit bir sınıftır. Aynı zamanda Laravel'in ForwardsCalls özelliğini kullanır, bu da çağrıların temel nesneye proxy yapılmasına olanak tanır.


Bu özelliğin iki yöntemi vardır: forwardCallTo ve forwardDecoratedCallTo . Dekore edilmiş nesne üzerinde yöntemleri zincirlemek istediğinizde ikincisini kullanın. Bizim durumumuzda her zaman tek bir çağrımız olacaktır.


Ayrıca özel değiştirici olan UniqueAndValidGenerator da uygulamamız gerekiyor, ancak makalenin amacı bu değil. Uygulamayla ilgileniyorsanız, bu sınıf temel olarak FakerPHP ile birlikte gelen ValidGenerator ve UniqueGenerator'ın bir karışımıdır, onu burada bulabilirsiniz.


Şimdi çerçeveyi AppServiceProvider genişletelim :

 <?php namespace App\Providers; use Closure; use Faker\Generator; use App\Support\FakerGenerator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->extend( $this->fakerAbstractName(), fn (Generator $base) => new FakerGenerator($base) ); } private function fakerAbstractName(): string { // This is important, it matches the name bound by the fake() helper return Generator::class . ':' . app('config')->get('app.faker_locale'); } }


extend() yöntemi, verilen adla eşleşen bir soyutun kapsayıcıya bağlanıp bağlanmadığını kontrol eder. Eğer öyleyse, kapatma sonucuyla değerini geçersiz kılar, bir göz atın:

 // Laravel: src/Illuminate/Container/Container.php public function extend($abstract, Closure $closure) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract])) { // You are interested here $this->instances[$abstract] = $closure($this->instances[$abstract], $this); $this->rebound($abstract); } else { $this->extenders[$abstract][] = $closure; if ($this->resolved($abstract)) { $this->rebound($abstract); } } }

Bu nedenle, kapsayıcıdaki fake() yardımcı bağlamalarıyla aynı adı üreten fakerAbstractName() yöntemini tanımladık.


Kaçırdıysanız yukarıdaki kodu tekrar kontrol edin, yorum bıraktım.


Artık, fake() öğesini her çağırdığımızda, FakerGenerator bir örneği döndürülecek ve tanıttığımız özel değiştiriciye erişimimiz olacak. FakerGenerator sınıfında bulunmayan bir çağrıyı her çağırdığımızda, __call() tetiklenecek ve forwardCallTo() yöntemini kullanarak onu temel Generator proxy olarak gönderecektir.


Bu kadar! Sonunda fake()->uniqueAndValid()->randomElement() işlevini yapabiliyorum ve harika çalışıyor!


Bitirmeden önce bunun saf bir dekoratör modeli olmadığını belirtmek isterim. Ancak desenler kutsal metinler değildir; ihtiyaçlarınıza uyacak ve sorunu çözecek şekilde bunları değiştirin.


Çözüm

Çerçeveler inanılmaz derecede faydalıdır ve Laravel birçok yerleşik özellik ile birlikte gelir. Ancak projelerinizdeki tüm uç durumları kapsayamazlar ve bazen çıkmaza girebilirsiniz. Bu olduğunda, çerçeveyi her zaman genişletebilirsiniz. Bunun ne kadar basit olduğunu gördük ve umarım bu Faker örneğinin ötesinde geçerli olan ana fikri anladınız.


Daima basit başlayın ve soruna en basit çözümü arayın. Karmaşıklık gerektiğinde gelecektir, dolayısıyla eğer temel kalıtım işe yararsa, bir dekoratör veya başka bir şey uygulamaya gerek yoktur. Çerçeveyi genişlettiğinizde, kaybın kazançtan daha ağır basacağı çok ileri gitmediğinizden emin olun. Çerçevenin bir bölümünü kendi başınıza sürdürmek istemezsiniz.