Filterable: Laravel için Temiz, Ölçeklenebilir Eloquent Filtreleme Kılavuzu
Filterable: Laravel için Temiz, Ölçeklenebilir Eloquent Filtreleme Kılavuzu
Laravel ile arama ve filtreleme özellikleri olan bir API geliştirdiyseniz, muhtemelen şu şekilde bir kod yazdınız:
$query = Post::query();
if ($request->status) {
$query->where('status', $request->status);
}
if ($request->title) {
$query->where('title', 'like', "%{$request->title}%");
}
if ($request->created_after) {
$query->where('created_at', '>=', $request->created_after);
}
// ...10 more conditions
Bu çalışma şekli, projeniz geliştikçe mantığın dağılımını ve tekrarı beraberinde getirir; test edilmesi imkânsız hale gelir.
Filterable, filtreleme mantığını özel, test edilebilir filtre sınıflarına taşıyarak bu sorunu çözer. Bu sayede dört farklı mühendislik yapısıyla mantığı ihtiyacınıza göre yapılandırabilirsiniz.
Kurulum
Kurulum
composer require kettasoft/filterable
php artisan vendor:publish --provider="Kettasoft\Filterable\Providers\FilterableServiceProvider" --tag="config"
İlk Filtrenizi Oluşturun
İlk Filtrenizi Oluşturun
CLI kullanarak bir filtre sınıfı oluşturun:
php artisan filterable:make-filter PostFilter --filters=title,status
Bu, App\Http\Filters\PostFilter sınıfını oluşturur. Açın ve filtre yöntemlerini tanımlayın:
namespace App\Http\Filters;
use Kettasoft\Filterable\Filterable;
use Kettasoft\Filterable\Support\Payload;
class PostFilter extends Filterable
{
protected $filters = ['status', 'title'];
protected function title(Payload $payload)
{
return $this->builder->where('title', 'like', $payload->asLike('both'));
}
protected function status(Payload $payload)
{
return $this->builder->where('status', $payload->value);
}
}
Kontrolcünüzde uygulayın:
public function index()
{
return Post::filter(PostFilter::class)->paginate();
}
Artık GET /posts?status=active&title=laravel isteği, gerekli yöntemleri otomatik olarak tetikler. Hiçbir “if” durumu ve manuel $request->has() kontrolü yok.
Filtreyi Modelinize Bağlama
Filtreyi Modelinize Bağlama
Her zaman aynı filtre sınıfını bir model için kullanıyorsanız, onu doğrudan bağlayabilirsiniz:
use Kettasoft\Filterable\Traits\HasFilterable;
class Post extends Model
{
use HasFilterable;
protected $filterable = PostFilter::class;
}
Artık kontrolcünüz yalnızca bir satırdan oluşur:
return Post::filter()->paginate();
Motor Seçimi
Motor Seçimi
Filterable, diğer filtreleme paketlerinden farklı olarak, dört motor sunar. İhtiyacınıza uygun olanı seçebilirsiniz.
| Motor | En İyi Kullanım Durumu | Örnek |
|---|---|---|
| Invokable | Alan başına özel mantık | ?status=active&title=laravel |
| Ruleset | Operatör bazlı API sorguları | ?filterStop Writing Filter Logic in Controllers — Meet Filterable for Laravel[like]=laravel |
| Expression | Ruleset + iç içe ilişkiler | ?filter[author.name][like]=ahmed |
| Tree | Karmaşık AND/OR JSON mantığı | { "and": [...] } |
Her birini inceleyelim.
1. Invokable Motoru
1. Invokable Motoru
Varsayılan motor. Her isteğin anahtarı, filtre sınıfınızdaki bir metoda karşılık gelir. Temiz, açık ve anlaşılır.
class PostFilter extends Filterable
{
protected $filters = ['status', 'title', 'created_at'];
protected function status(Payload $payload)
{
return $this->builder->where('status', $payload->value);
}
protected function title(Payload $payload)
{
return $this->builder->where('title', 'like', $payload->asLike('both'));
}
protected function created_at(Payload $payload)
{
return $this->builder->whereDate(, , $payload->value);
}
}
Ayrıca, bu yöntemlere davranış eklemek için PHP 8 Annotations kullanabilirsiniz:
use Kettasoft\Filterable\Annotations\Cast;
use Kettasoft\Filterable\Annotations\SkipIf;
use Kettasoft\Filterable\Annotations\Between;
class PostFilter extends Filterable
{
protected $filters = [, ];
#[Cast('integer')]
#[DefaultValue(1)]
protected function status(Payload $payload)
{
return $this->builder->where(, ->value);
}
#[SkipIf('auth()->guest()')]
#[Between(min: '2020-01-01', max: 'now')]
protected function created_at(Payload $payload)
{
return ->builder->whereDate(, ->value);
}
}
Kullanılabilir anotasyonlar: #[Authorize] #[SkipIf] #[Cast] #[Sanitize] #[Trim] #[DefaultValue] #[MapValue] #[Explode] #[Required] #[In] #[Between] #[Regex] #[Scope]
2. Ruleset Motoru
2. Ruleset Motoru
REST API’leri için idealdir, burada ön yüz yapısal filtre parametreleri ile açıkça operatörler gönderir.
GET /posts?filter[status]=published
GET /posts?filterStop Writing Filter Logic in Controllers — Meet Filterable for Laravel[like]=%laravel%
GET /posts?filter[views][gte]=100
GET /posts?filter[id][in][]=1&filter[id][in][]=2
GET /posts?filter[price][between][]=10&filter[price][between][]=50
Filtre sınıfınızı izin verilen alanlar ve operatörler ile tanımlayın:
class PostFilter extends Filterable
{
protected $allowedFields = [, , , ];
protected $allowedOperators = [, , , , , ];
}
Yöntemlere ihtiyaç yok — motor otomatik olarak sorgu oluşturmayı yönetir.
Desteklenen operatörler: eq neq gt gte lt lte like nlike in nin between null notnull
3. Expression Motoru
3. Expression Motoru
Ruleset motorunun yaptığı her şey, ayrıca iç içe Eloquent ilişkileri kullanarak filtreleme yeteneği sağlar.
GET /posts?filter[author.profile.name][like]=ahmed
GET /posts?filter[status]=active&filter[category.name]=laravel
use Kettasoft\Filterable\Facades\Filterable;
Filterable::create()
->useEngine()
->allowedFields([, ])
->allowRelations([
[, ]
])
->filter(Post::query());
Motor, ilişki yolunu otomatik olarak çözer ve whereHas sorgularını uygular — manuel bir birleştirme mantığına gerek yoktur.
4. Tree Motoru
4. Tree Motoru
İleri düzey kullanım senaryoları için, ön yüz karmaşık AND/OR mantıkları oluşturması gerektiğinde — admin panelleri veya raporlama araçları düşünün.
İstek, iç içe bir JSON ağacı gönderir:
{
"filter": {
"and": [
{ "field": , "operator": , "value": },
{
"or": [
{ "field": , "operator": , "value": 1000 },
{ "field": , "operator": , "value": true }
]
}
]
}
}Motor, bunu tekrar tekrar Eloquent where / orWhere gruplarına çevirir. Ayrıca, aşırı karmaşık sorguları önlemek için derinlik sınırlaması destekler:
// config/filterable.php
'tree' => [
=> 3,
=> ,
]
Validasyon & Sanitizasyon
Validasyon & Sanitizasyon
Veri giriş işlemleri filtre sınıfı içinde gerçekleşir, kontrolcüler veya form istekleri arasında dağılmamıştır.
Validasyon, Laravel’in yerel kural formatını kullanır:
class PostFilter extends Filterable
{
protected $rules = [
=> [, , ],
=> [, , ],
];
}
Validasyon hatası oluşursa, automatik olarak bir ValidationException fırlatılır.
Sanitizasyon, validasyondan önce çalışır ve özel sanitizasyon sınıfları ile:
class PostFilter extends Filterable
{
protected $sanitizers = [
TrimSanitizer::class, // global — runs on all fields
=> [
StripTagsSanitizer::class,
CapitalizeSanitizer::class,
],
];
}
Bir sanitizasyon, Sanitizable arayüzünü implement eder:
class TrimSanitizer implements Sanitizable
{
public function sanitize(mixed $value): mixed
{
return is_string() ? trim() : ;
}
}
İşlem sırası: sanitize → validate → filter.
Yetkilendirme
Yetkilendirme
Rol veya izinlere dayanarak bir filtre sınıfını korumak için authorize() metodunu tanımlayın:
class AdminFilter extends Filterable
{
public function authorize(): bool
{
return auth()->user()?->isAdmin() ?? ;
}
}
authorize() false dönerse, herhangi bir filtreleme çalışmadan önce bir FilterAuthorizationException fırlatılır. Her metoda göre yetkilendirme, Invokable motorunda #[Authorize] anotasyonu ile de mümkündür.
Sıralama
Sıralama
Hangi alanların sıralanabilir olduğunu $sortable ile tanımlayın:
class PostFilter extends Filterable
{
protected $sortable = [, , ];
}
GET /posts?sort=-created_at # azalan
GET /posts?sort=views # artan
Önbellekleme
Önbellekleme
Filterable, filtreleme boru hattında tamamen bir önbellekleme sistemi sunar.
// 1 saat süreyle önbellekle
Post::filter()->cache(3600)->get();
// Kullanıcı bazlı - her kullanıcı kendi önbellek girişine sahip olur
Post::filter()->cache(1800)->scopeByUser()->get();
// Etiketlenmiş önbellek - gruplar halinde kolayca geçersiz kılınabilir
Post::filter()->cache(3600)->cacheTags([, ])->get();
Post::flushCacheByTagsStatic([]);
// Koşullu önbellekleme
Post::filter()->cacheWhen(!auth()->user()->isAdmin(), 3600)->get();
// Config'de tanımlı yeniden kullanılabilir önbellek profilleri
Report::filter()->cacheProfile()->get();
Otomatik geçersiz kılmayı config/filterable.php içerisinde etkinleştirerek, bir model oluşturulduğunda, güncellendiğinde veya silindiğinde, önbellek otomatik olarak temizlenir:
=> [
=> ,
=> [
\App\Models\Post::class => [, ],
],
],
CLI Referansı
CLI Referansı
# Yeni bir filtre sınıfı oluştur
php artisan filterable:make-filter PostFilter --filters=title,status
# Uygulamanızdaki tüm filtre sınıflarını keşfedin ve kaydedin
php artisan filterable:discover
# Tüm kayıtlı filtreleri listeleyin
php artisan filterable:list
# Bir filtreyi örnek bir payload ile test edin
php artisan filterable:test PostFilter --payload=
# Bir filtre sınıfını inceleyin (motor, alanlar, kurallar, sanitizasyon vb.)
php artisan filterable:inspect PostFilter
Sonuç
Sonuç
Filterable, kontrolcülerinizin karmaşasına neden olmayacak şekilde Eloquent filtrelemeyi yapılandırılmış, test edilebilir bir şekilde yönetmenizi sağlar. İster basit anahtar-değer filtreleri, ister karmaşık iç içe AND/OR ağaçları olsun, her biri için bir motor mevcuttur — ve hepsi aynı validasyon, sanitizasyon, yetkilendirme, önbellekleme ve sıralama altyapısını paylaşmaktadır.
Bağlantılar:
Yapan Kettasoft. MIT Lisansı altındadır.
Kaynak: Orijinal Makale


