Bir Laravel event, uygulamanızda "bir şey oldu" bilgisini yayınlamanın ve bu olaya tepki verecek kodu tamamen ayrı tutmanın en temiz yoludur. Kullanıcı kaydolduğunda hoş geldin maili gönderen, bir log düşen ve istatistik güncelleyen üç ayrı işi tek bir controller içine tıkıştırmak yerine, tek bir olay yayınlarsınız ve bu olayı dinleyen listener'lar işi paylaşır. Sonuç: gevşek bağlı, test edilebilir ve büyümeye açık bir kod tabanı. Bu yazıda event/listener mimarisini sıfırdan kurmayı, kuyruğa almayı ve gerçek senaryolarda nasıl kullanacağınızı adım adım göreceğiz.
Event ve Listener mantığı nedir?
Fikir basit: bir event, "olan bir şeyi" temsil eden bir mesaj nesnesidir — örneğin OrderShipped (sipariş kargolandı). Bir listener ise bu olaya tepki veren koddur — kullanıcıya mail atmak gibi. Event'i yayınlayan kod, kimin dinlediğini bilmek zorunda değildir. Bu, klasik bir "yayıncı/abone" (publish/subscribe) desenidir ve birkaç önemli avantaj sağlar:
- Gevşek bağ: Controller, mail mi atıldığını yoksa log mu düşüldüğünü bilmez. Yeni bir tepki eklemek için yalnızca yeni bir listener yazarsınız.
- Tek sorumluluk: Her listener tek bir işi yapar; sınıflar küçük ve okunabilir kalır.
- Kolay test: Event'in yayınlandığını tek satırda doğrulayabilirsiniz.
Event ve Listener oluşturmak
Her ikisini de artisan ile üretebilirsiniz. Listener'ı oluştururken --event bayrağıyla hangi olayı dinleyeceğini bildirmek işinizi kolaylaştırır:
php artisan make:event OrderShipped
php artisan make:listener SendShipmentNotification --event=OrderShipped
Event sınıfı genellikle yalnızca veri taşır. İlgili modeli yapıcıya geçirmek yeterlidir:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped
{
use Dispatchable, SerializesModels;
public function __construct(public Order $order) {}
}
Listener ise handle() metodunda asıl işi yapar. Type-hint ettiğiniz parametre, hangi event'i dinlediğinizi belirler:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use App\Notifications\ShipmentNotification;
class SendShipmentNotification
{
public function handle(OrderShipped $event): void
{
$event->order->user->notify(
new ShipmentNotification($event->order)
);
}
}
Listener'ları kaydetmek
Laravel 11 ve 12'de listener'lar otomatik keşfedilir. app/Listeners klasöründeki bir sınıfın handle() metoduna bir event type-hint ettiğinizde, çerçeve bu bağı kendisi kurar; ekstra kayıt yapmanıza gerek yoktur. Bu, eski sürümlerdeki EventServiceProvider içindeki $listen dizisine olan ihtiyacı ortadan kaldırır.
Yine de manuel kayıt gerektiren durumlar olur — örneğin bir kapanış (closure) ile hızlıca dinlemek isterseniz. Bunu AppServiceProvider'ın boot() metodunda yapabilirsiniz:
use App\Events\OrderShipped;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(function (OrderShipped $event) {
// hızlı, tek seferlik bir tepki
});
}
Event'i dispatch etmek
Bir olayı yayınlamak için dispatch statik metodunu ya da global event() yardımcısını kullanırsınız. İkisi de aynı işi yapar:
use App\Events\OrderShipped;
// statik metot
OrderShipped::dispatch($order);
// ya da event() yardımcısı
event(new OrderShipped($order));
Bu satır çalıştığı anda, OrderShipped'i dinleyen tüm listener'lar tek tek tetiklenir. Controller'ınız sadece olayı yayınlar; gerisi listener'ların işidir. Tipik bir kullanım şöyle görünür:
public function ship(Order $order)
{
$order->markAsShipped();
OrderShipped::dispatch($order);
return back()->with('status', 'Sipariş kargolandı.');
}
Listener'ları kuyruğa almak
Mail göndermek gibi yavaş işler isteği bekletmemeli. Bir listener'ı arka plana taşımak için tek yapmanız gereken ShouldQueue arayüzünü uygulamaktır:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
public int $tries = 3;
public int $backoff = 30;
public function handle(OrderShipped $event): void
{
// arka planda çalışır
}
}
Artık bu listener bir kuyruğa atılır ve worker tarafından işlenir. $tries yeniden deneme sayısını, $backoff denemeler arasındaki bekleme süresini (saniye) belirler. Kuyruğun çalışması için bir worker'ı arka planda tutmanız gerekir:
php artisan queue:work
Listener kalıcı olarak başarısız olduğunda devreye girecek bir failed() metodu da ekleyebilir, burada yöneticiye bildirim atabilir veya log düşebilirsiniz. Önemli ayrıntı: kuyruğa alınan listener'lar event nesnesini serileştirir, bu yüzden SerializesModels trait'i sayesinde modelin yalnızca anahtarı saklanır ve iş çalışırken veritabanından taze yeniden yüklenir.
Event subscriber ile listener'ları gruplamak
Tek bir konuyla ilgili birden çok dinleyiciyi tek sınıfta toplamak isterseniz subscriber kullanabilirsiniz. Subscriber, bir subscribe() metoduyla kendi olay eşlemelerini kaydeden bir sınıftır:
<?php
namespace App\Listeners;
use Illuminate\Events\Dispatcher;
class OrderEventSubscriber
{
public function handleShipped($event): void { /* ... */ }
public function handleCancelled($event): void { /* ... */ }
public function subscribe(Dispatcher $events): array
{
return [
\App\Events\OrderShipped::class => 'handleShipped',
\App\Events\OrderCancelled::class => 'handleCancelled',
];
}
}
Bu yaklaşım, sipariş yaşam döngüsü gibi birbiriyle ilişkili olayları tek bir okunabilir yerde tutmak için idealdir.
Sık Sorulan Sorular
Event/listener mi yoksa job mu kullanmalıyım?
İkisi birbirini tamamlar. Bir job, "şu işi arka planda yap" demektir; tek ve belirli bir görevdir. Event ise "şu oldu" demektir ve birden fazla bağımsız tepkiyi tetikleyebilir. Tek bir görev varsa doğrudan job; tek bir olaya birden çok tarafın tepki vermesi gerekiyorsa event/listener kullanın (ve listener'ı kuyruğa alarak job avantajını da elde edin).
Bir testte event'in yayınlandığını nasıl doğrularım?
Event::fake() ile olayları sahteye alır, sonra Event::assertDispatched(OrderShipped::class) ile yayınlandığını doğrularsınız. Böylece gerçek listener'ları çalıştırmadan yalnızca olayın tetiklendiğini test edebilirsiniz.
Model event'leri ile bunların ilişkisi nedir?
Eloquent, created, updated, deleted gibi kendi model event'lerini otomatik yayınlar. Bunları bir observer sınıfıyla dinlemek, model yaşam döngüsüne bağlı işler için (örneğin oluşturulurken slug üretmek) çoğu zaman daha temizdir. Daha geniş, iş mantığına dair olaylar içinse kendi event sınıflarınızı yazmak daha doğrudur.
Kodunuzu gevşek bağlı hale getirmek istiyorsanız mevcut controller'larınızdaki kalabalık mantığı birlikte event ve listener'lara ayırabiliriz; Laravel mimarinizi sadeleştirmek için benimle iletişime geçin.