Laravel’de Tasarım Desenleri — Pipeline Pattern

Ali Özgür
istanbulphp
Published in
4 min readNov 15, 2023

Selamlar, bu yazımda sizlere Laravel’deki tasarım desenlerinden biri olan Pipeline Pattern’ın nasıl kullanıldığını, hangi durumlarda işimize yarayabileceğini ufak örnekler ile anlatacağım.

Pipeline pattern, bir aksiyon alınması gerektiğinde sıralı şekilde işlemler çalıştırmamız gereken durumlarda codebase’de oluşan karmaşıklıkları önleyip daha temiz kod yazmamızı sağlayabilecek bir kalıptır.

Bu kalıp, direkt olarak Laravel’in bir özelliği olmasına rağmen Laravel’in resmi dökümanında bulunmuyor. Bu yüzden kodlarımızı çok daha temize çıkaracak olan bu kalıp bazı developerlar tarafından bilinmiyor.

Laravel Pipeline kalıbının çalışma örneği

Nerelerde kullanabiliriz?

Veritabanına bir kayıt eklediğimizde bazı süreçlerden geçip verinin işlenmesi gerekiyorsa veya bir model oluşturduğumuzda belirli aksiyonlar alınması gerekiyorsa pipeline pattern kullanabiliriz.

Kompleks ve uzun işlemleri birbirinden ayrı pipeline sınıflarında teker teker ele alacağımız için hem kod tekrarından kaçınmış olacağız hem de gelecekte uzun uzun servis ve controller sınıfları ile başa çıkmamız gerekmeyecek.

Nasıl kullanacağız?

Öncelikle gerçekleştirmek istediğimiz işlemleri tek tek ayrıştırmamız gerekiyor. Daha sonra her işlem için bir sınıf oluşturup hangi işlemlerin birbirine bağımlı olduğunu belirleyip işlemlerin sıralamasını da buna göre yapmamız gerekiyor.

Eğer sıralamayı ilk başta doğru kurarsak yeni işlem eklenmesi veya mevcut işlemlerin pipeline’dan çıkarılması durumlarında herhangi bir sorun yaşamayız.

Pipeline kalıbı kullanarak hangi durumdan kaçınacağız?

return createOGContent(createMetaTags(addToSitemap(createSlug($data))));

şeklinde uzun uzadıya çağırıp kodu okumayı ve kodun sürdürülmesini zorlaştıran kod parçalarını görmüşüzdür. Pipeline kalıbı kullanarak bu sıralı işlemleri daha düzenli hale getirmeyi hedefliyoruz.

Nasıl kullanılacağına dair örneklendirme:

Göstereceğim örnekte bir blog post oluşturulduktan sonra SEO tarafı ile ilgili işlemlerin yapılmasına dair bir pipeline yer alıyor.

Öncelikle bütün kodu controller sınıfına yığmamak için bir servis sınıfı oluşturuyorum. Controller sınıflarındaki uzun metodları daha basit hale getirmek için Mücahit Cücen’in ilgili makalesine göz atabilirsiniz.

class BlogPostService
{
public function handle(array $data): BlogPost
{
return app(Pipeline::class)
->send($data)
->through([
CreateSlug::class,
AddToSitemap::class,
CreateMetaTags::class,
CreateOgContent::class,
])->then(function ($data) {
return BlogPost::create($data);
});
}
}

Bu metodda öncelikle pipelineımızın kurulumunu gerçekleştiriyoruz. Pipeline sınıfı direkt olarak Laravel’in içinde geliyor ve ek bir paket kurmanıza gerek yok.

send() metodu pipelineımızın geçeceği aşamalarda kullanılacak olan veriyi göndermemize yarıyor. Bu metod mixed olarak değişken alınıyor, bu nedenle isterseniz direkt olarak modelinizi, bir DTO ve hatta eloquent query bile verip pipelar içerisinde işlemlerinizi gerçekleştirebilirsiniz.

through() metodu ile gönderdiğimiz verinin işleneceği sınıfları belirliyoruz. Eğer düzenleyeceğiniz veriler birbirine bağımlı ise daha önce de bahsettiğim gibi sınıfları doğru sıraya koymanız gerekiyor.

then() metodu ile verimizin tüm aşamalardan geçtikten sonra ne yapılacağını closure içinde verdiğimiz metod. Blog postumuzu oluşturmak için işlediğimiz $data değişkenimizi eloquent model ile create edip işlemimizi tamamlıyoruz.

Eğer pipeline’ın sonunda dönülmesi gereken veriyi tekrar kullanmanız gerekiyorsa thenReturn() metodunu kullanarak bu işlemi gerçekleştirebilirsiniz. Daha basit bir kod yazmak için create işlemini direkt olarak then() metodunun içinde gerçekleştirdim.

Pipeline içinde işlem yapacağımız sınıflar:

İşlem yapacağımız sınıfları Pipelines\BlogPost dizini altında oluşturuyorum. Bunun için yapısal olarak herhangi bir kural bulunmuyor, bu yüzden istediğiniz gibi dizinlendirebilirsiniz.

class CreateSlug
{
public function handle($post, Closure $next)
{
$post['slug'] = Str::slug($post['title']);

return $next($post);
}
}

Pipeline, through() metodu üzerinden verdiğimiz sınıflardaki handle() metodunu çağırıyor. Bu metoda verilen birinci değişken gönderdiğimiz veri oluyor ve bir tür kısıtı bulunmuyor.

CreateSlug sınıfına gönderdiğim veri Array olduğu için uygun şekilde slug verisini ekliyorum. $post içinde title değerine sahibiz fakat slug değeri bulunmuyor. Laravel String Helper’ın sağladığı slug metodu ile bu title değerini kullanarak bir slug oluşturuyorum. Ardından düzenlemiş olduğum $post değişkeni ile bir sonraki işlemlere devam etmesini sağlıyorum.

Oluşturduğum slug’ı site haritamı güncellerken URL verebilmek için kullanmam gerekiyor. Site haritamı güncellemek için ise AddToSitemap isminde yeni bir sınıf oluşturuyorum.

class AddToSitemap
{
public function handle($post, Closure $next)
{
$sitemap = Storage::get('public/sitemap.xml');

$url = url("/posts/{$post['slug']}");
$today = date('Y-m-d');

$sitemap = str_replace('</urlset>', "
<url>
<loc>{$url}</loc>
<lastmod>{$today}</lastmod>
</url>
</urlset>", $sitemap);

Storage::put('public/sitemap.xml', $sitemap);

return $next($post);

}
}

Birebir slug oluşturduğum sınıfta kullandığım handle() metodunu bu sınıfta da kullanıyorum. Diğer pipe’ta yeni oluşturduğum slug değerini aynı $post değişkeninden alıp burada kullanabiliyorum.

Son olarak bu servisi controller üzerinde çağırıp işlemlerimi tamamlıyorum.

class BlogPostController extends Controller
{
public function store(CreateBlogPostRequest $request, BlogPostService $blogPostService): JsonResponse
{
$blogPost = $blogPostService->handle($request->validated());

return response()->json([
'data' => $blogPost,
], Response::HTTP_CREATED);
}
}

Bu durumda hem controller hem de servis sınıflarımdaki kod yapısı çok fazla uzamadan örnekte ihtiyaç duyulan işlemleri gerçekleştirebildik.

Sonuç

Sadece title ve content parametrelerini aldığımız blog post oluşturma servisimizde pipeline kalıbı kullanarak basit bir şekilde SEO ile ilgili ihtiyaç duyulabilecek bazı verileri oluşturmayı başardık.

Örnekteki gibi ufak bir işlem için bu yapıyı kurmak yorucu gelebilir fakat daha büyük logiclere sahip işlemleriniz için pipeline kalıbı kullanırsanız kompleks servislerinizi okuması daha basit ve daha sürdürülebilir kodlara dökebilirsiniz.

Pipeline Pattern ile ilgili Laravel’in API dökümanı: https://laravel.com/api/9.x/Illuminate/Pipeline/Pipeline.html

Laravel’in mimarı olan Taylor Otwell’in konuyla ilgili bir tweeti:

https://twitter.com/taylorotwell/status/1064629935954935809

--

--