Kategoriler
Teknik

PHP’de Özyinelemeli Closure Kullanımı

Bazen bir çözümü gerçekleştirmek için özyinelemeli bir fonksiyon veya metot kullanma ihtiyacımız olabiliyor. Bu ihtiyaç, benim bir kategoriye ait öğeleri ve o kategorinin alt kategorisine ait öğeleri de içine alacak şekilde Eloquent ORM’de listelemem gerektiğinde ortaya çıktı.

Bunun için, kategori modeline alt kategorilerinin ID’sini getiren bir metot ekledim. İşte bu metodun örneği:

<?php

declare(strict_types=1);

namespace App\Models;

// things...

class Category
{
    public function categories()
    {
        return $this->hasMany(Category::class);
    }

    public function getChildCategoryIDs(): array
    {
        $ids = [];

        $extract = function (Category $category, array &$ids = []) use (&$extract): void {
            $ids[] = $category->id;

            if (isset($category->categories) && is_iterable($category->categories)) {
                foreach ($category->categories as $category) {
                    $extract($category, $ids);
                }
            }
        }

        $extract($this);

        return $ids;
    }
}

Closure Kullanımı: Neden?

Bu örnekte, özyinelemeyi gerçekleştirmek için sınıf içerisinde bir metot oluşturabilirdim. Ancak bu yaklaşımın, metot sayısını artıracağı ve kodun karmaşıklığını potansiyel olarak artırabileceği göz önünde bulundurulduğunda, tüm işlemleri tek bir metot içinde gerçekleştirmeye karar verdim ve Closure’u tercih ettim.

Kategori Yapısının Sebepleri

Bu kategori yapısını, ekstra bir Laravel/PHP paketi kullanmadan ve veritabanı tablo yapısını basit tutmak amacıyla oluşturdum. Alternatif olarak, Laravel’de hiyerarşik kategori paketi olan Baum’u kullanabilirdim, ancak bu durumda ek bir bağımlılığın getireceği potansiyel karmaşıklığı tercih etmedim.

Bu yaklaşımın kısa vadede hızlı ve basit bir çözüm sağladığını, ancak uzun vadede ve artan web trafiğiyle birlikte sorgu optimizasyonu ihtiyacını doğurabileceğini unutmamak önemli. Ancak, bu potansiyel sorun, önbellek kullanımıyla çözülebilir.

Çalışma Şekli:

<?php

public function show(Category $category)
{
    $category->load('categories.categories.categories');
    $ids = $category->getChildCategoryIDs();
    $products = Product::whereIn('category_id', $ids)->get();

    return view('products.index', compact('category', 'products'));
}

CategoryController içerisindeki show metodu, belirli bir kategoriyi ve o kategorinin ürünlerini listeleyen bir işlem gerçekleştirir. ‘Lazy load’ yöntemi kullanarak, hedef kategorinin alt kategorilerini (burada üç dal alıyoruz) elde ederiz ve model içinde tanımladığımız getChildCategoryIDs metodu ile bu alt kategorilerin ID’lerini alırız. Son olarak, bu ID’lere sahip tüm ürünlerin bir listesini WHERE IN sorgusu ile elde ederiz.