Event در Laravel؛ راهنمای کامل رویدادها
Event در Laravel یکی از ابزارهای مهم برای طراحی نرمافزارهای تمیز، توسعهپذیر و مبتنی بر رویداد است. با استفاده از Event و Listener میتوان اتفاقات مهم سیستم، مثل ثبت سفارش، پرداخت موفق، ثبتنام کاربر، تغییر وضعیت فاکتور یا انتشار مقاله را از منطقهای جانبی مثل ارسال ایمیل، پیامک، اعلان، ثبت لاگ، پاکسازی Cache و همگامسازی با سیستمهای خارجی جدا کرد. این مقاله یک راهنمای کامل و فنی درباره Event در Laravel است و مفاهیمی مثل Event، Listener، Event Discovery، Queued Listener، Event Subscriber، Eloquent Event، Event Broadcasting، تستنویسی و بهترین روشهای معماری را برای پروژههای حرفهای توضیح میدهد.
برای شنیدن متن، روی «پخش صوت مقاله» بزنید.
مقدمه
در پروژههای نرمافزاری واقعی، بسیاری از عملیاتها فقط یک کار ساده نیستند. برای مثال، وقتی یک سفارش ثبت میشود، شاید لازم باشد پیامک ارسال شود، ایمیل تأیید سفارش فرستاده شود، موجودی انبار بهروزرسانی شود، لاگ فعالیت ثبت شود، امتیاز کاربر محاسبه شود، اعلان برای مدیر ارسال شود و دادهها با یک CRM خارجی همگامسازی شوند. اگر همه این کارها را مستقیم داخل Controller یا Service ثبت سفارش بنویسیم، کد خیلی سریع شلوغ، وابسته، سختتست و سختنگهداری میشود.
اینجاست که Event در Laravel اهمیت پیدا میکند. Event به ما کمک میکند بگوییم «یک اتفاق مهم در سیستم رخ داده است» و سپس بخشهای دیگر برنامه، بدون اینکه منطق اصلی را شلوغ کنند، به آن اتفاق واکنش نشان دهند. برای مثال، بعد از ثبت سفارش میتوان Eventی به نام OrderCreated منتشر کرد و چند Listener جداگانه برای ارسال ایمیل، ارسال پیامک، ثبت لاگ و اطلاعرسانی به مدیر تعریف کرد.
در مستندات رسمی Laravel، سیستم Event بهعنوان پیادهسازی سادهای از الگوی Observer معرفی شده که اجازه میدهد برای رویدادهای مختلف برنامه Subscribe و Listen کنیم. Eventها معمولاً در مسیر app/Events و Listenerها در مسیر app/Listeners قرار میگیرند و این پوشهها هنگام ساخت Event و Listener با Artisan ایجاد میشوند. مطالعه مستندات رسمی Laravel درباره Events
برای یک شرکت تولید نرمافزار، استفاده صحیح از Eventها فقط یک قابلیت فنی نیست؛ بلکه بخشی از معماری حرفهای است. Eventها باعث کاهش وابستگی، افزایش توسعهپذیری، بهبود تستپذیری و آماده شدن پروژه برای رشد میشوند. در این مقاله بهصورت کامل و فنی بررسی میکنیم Event در Laravel چیست، چه تفاوتی با Job و Observer دارد، چگونه ساخته میشود، چطور Listenerها اجرا میشوند، چه زمانی باید آنها را Queue کرد، Broadcasting چیست و در پروژههای شرکتی چه Best Practiceهایی باید رعایت شود. ⚡
Event در Laravel چیست؟
Event در Laravel یک کلاس PHP است که نشان میدهد یک اتفاق مهم در سیستم رخ داده است. Event معمولاً خودش کار سنگینی انجام نمیدهد؛ بلکه فقط اطلاعات مربوط به آن اتفاق را حمل میکند. سپس یک یا چند Listener به آن Event گوش میدهند و کارهای جانبی لازم را انجام میدهند.
برای مثال، در یک فروشگاه اینترنتی:
اتفاق: سفارش ثبت شد
Event: OrderCreated
Listenerها:
- SendOrderCreatedEmail
- SendOrderCreatedSms
- NotifyAdminAboutNewOrder
- AddOrderActivityLog
- SyncOrderWithCrm
در این مدل، منطق اصلی ثبت سفارش مستقل از منطقهای جانبی باقی میماند. Service ثبت سفارش فقط سفارش را ایجاد میکند و Event را منتشر میکند. Listenerها مسئول واکنشها هستند.
جریان کلی:
Controller → Service → Event Dispatch → Listener 1
→ Listener 2
→ Listener 3
این الگو باعث میشود کدها ماژولارتر شوند. اگر بعداً بخواهید بعد از ثبت سفارش یک پیام جدید در Slack ارسال کنید، لازم نیست منطق ثبت سفارش را تغییر دهید؛ کافی است یک Listener جدید برای Event OrderCreated اضافه کنید.
چرا Eventها در Laravel مهم هستند؟
استفاده از Eventها مزایای معماری زیادی دارد؛ مخصوصاً در پروژههای متوسط و بزرگ.
| مزیت | توضیح |
|---|---|
| کاهش وابستگی | کد اصلی لازم نیست همه واکنشهای جانبی را بشناسد |
| توسعهپذیری بیشتر | اضافه کردن رفتار جدید با Listener جدید انجام میشود |
| Controller و Service تمیزتر | منطق جانبی از جریان اصلی جدا میشود |
| تستپذیری بهتر | میتوان Event Dispatch شدن یا Listenerها را جداگانه تست کرد |
| مناسب Queue | Listenerهای زمانبر میتوانند در صف اجرا شوند |
| معماری رویدادمحور | سیستم بر اساس اتفاقات مهم دامنه طراحی میشود |
| مناسب کار تیمی | تیمها میتوانند Listenerهای مستقل توسعه دهند |
| آمادگی برای Broadcasting | بعضی Eventها میتوانند به Frontend بهصورت Real-time ارسال شوند |
بدون Event، Serviceها و Controllerها معمولاً پر از کارهای جانبی میشوند:
$order = Order::create($data);
Mail::to($user)->send(new OrderCreatedMail($order));
Sms::send($user->mobile, 'سفارش شما ثبت شد.');
ActivityLog::create([...]);
Crm::syncOrder($order);
Cache::forget('dashboard_stats');
با Event:
$order = $this->orderService->create($data);
OrderCreated::dispatch($order);
حالا هر کار جانبی در Listener خودش قرار میگیرد.
تفاوت Event و Listener چیست؟
Event و Listener دو بخش مکمل هستند.
| مفهوم | نقش | مثال |
|---|---|---|
| Event | اعلام میکند یک اتفاق رخ داده است | OrderCreated |
| Listener | به Event واکنش نشان میدهد | SendOrderCreatedNotification |
Event معمولاً شامل داده مربوط به اتفاق است:
class OrderCreated
{
public function __construct(
public Order $order
) {}
}
Listener آن داده را دریافت و پردازش میکند:
class SendOrderCreatedNotification
{
public function handle(OrderCreated $event): void
{
// Send notification using $event->order
}
}
در معماری حرفهای، Event باید «اتفاق» را توصیف کند، نه «کاری که باید انجام شود». برای مثال:
نام خوب برای Event:
OrderCreated
PaymentSucceeded
UserRegistered
InvoicePaid
PostPublished
نام ضعیف برای Event:
SendOrderEmail
UpdateDashboardCache
CallCrmApi
چون Event باید رخداد را اعلام کند، نه دستور انجام یک کار خاص را.
ساخت Event در Laravel
برای ساخت Event از Artisan استفاده میکنیم:
php artisan make:event OrderCreated
این دستور معمولاً فایلی در مسیر زیر ایجاد میکند:
app/Events/OrderCreated.php
نمونه Event:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderCreated
{
use Dispatchable, SerializesModels;
public function __construct(
public Order $order
) {}
}
در این کلاس چند نکته مهم وجود دارد:
| بخش | کاربرد |
|---|---|
| Dispatchable | امکان Dispatch کردن Event را سادهتر میکند |
| SerializesModels | هنگام Queue شدن، مدلهای Eloquent را بهتر Serialize میکند |
| Constructor | داده مورد نیاز Event را دریافت میکند |
| Public Property | Listenerها میتوانند به داده Event دسترسی داشته باشند |
در این مثال، Event حاوی یک سفارش است. Listenerها میتوانند از طریق $event->order به سفارش دسترسی داشته باشند.
ساخت Listener در Laravel
برای ساخت Listener از دستور زیر استفاده میکنیم:
php artisan make:listener SendOrderCreatedNotification --event=OrderCreated
فایل Listener معمولاً در مسیر زیر قرار میگیرد:
app/Listeners/SendOrderCreatedNotification.php
نمونه:
<?php
namespace App\Listeners;
use App\Events\OrderCreated;
use App\Services\NotificationService;
class SendOrderCreatedNotification
{
public function handle(OrderCreated $event): void
{
// Access order: $event->order
}
}
نسخه حرفهایتر با Dependency Injection:
<?php
namespace App\Listeners;
use App\Events\OrderCreated;
use App\Services\NotificationService;
class SendOrderCreatedNotification
{
public function __construct(
protected NotificationService $notificationService
) {}
public function handle(OrderCreated $event): void
{
$this->notificationService->sendOrderCreated($event->order);
}
}
در مستندات رسمی Laravel آمده است که Listenerها از طریق Service Container resolve میشوند؛ بنابراین میتوانند وابستگیهای مورد نیاز خود را در Constructor type-hint کنند و Laravel آنها را خودکار تزریق میکند. مطالعه مستندات رسمی Laravel درباره Events و Listeners
Dispatch کردن Event
بعد از ساخت Event، میتوان آن را در هر بخش از برنامه Dispatch کرد. برای مثال، داخل Service ثبت سفارش:
use App\Events\OrderCreated;
class OrderService
{
public function create(array $data): Order
{
$order = Order::create($data);
OrderCreated::dispatch($order);
return $order;
}
}
یا با helper:
event(new OrderCreated($order));
روش جدیدتر و خواناتر معمولاً استفاده از متد dispatch روی خود Event است:
OrderCreated::dispatch($order);
نکته مهم: Event را بهتر است بعد از انجام موفق عملیات اصلی منتشر کنید. اگر عملیات اصلی داخل Transaction دیتابیس است، باید دقت کنید Event یا Listener وابسته به داده Commitشده زودتر از موعد اجرا نشود. در بخشهای بعدی این موضوع را دقیقتر بررسی میکنیم.
ثبت Event و Listener در Laravel
در نسخههای جدید Laravel، قابلیت Event Discovery اهمیت زیادی دارد. طبق مستندات رسمی Laravel، بهصورت پیشفرض Laravel Listenerهای برنامه را با اسکن دایرکتوری Listeners پیدا و ثبت میکند؛ هر متدی که با handle یا __invoke شروع شود و Event را در signature خود type-hint کرده باشد، بهعنوان Listener آن Event ثبت میشود. مطالعه مستندات رسمی Laravel درباره Event Discovery
یعنی اگر Listener شما اینگونه باشد:
class SendOrderCreatedNotification
{
public function handle(OrderCreated $event): void
{
//
}
}
Laravel میتواند تشخیص دهد که این Listener برای Event OrderCreated است.
در برخی پروژهها، ممکن است Eventها و Listenerها بهصورت دستی در EventServiceProvider ثبت شوند. این روش در نسخههای قدیمیتر رایجتر بود یا زمانی استفاده میشود که ساختار پروژه خاص است.
نمونه مفهومی:
protected $listen = [
OrderCreated::class => [
SendOrderCreatedNotification::class,
NotifyAdminAboutNewOrder::class,
],
];
در پروژههای جدید، Event Discovery معمولاً ساختار را سادهتر میکند؛ اما در پروژههای بزرگ، تیم باید تصمیم بگیرد از Discovery استفاده کند یا ثبت صریح را ترجیح میدهد. ثبت صریح خوانایی بیشتری در نقشه رویدادها میدهد، اما Discovery کدنویسی تکراری را کاهش میدهد.
Event در معماری تمیز Laravel
Eventها زمانی ارزش واقعی پیدا میکنند که در جای درست معماری استفاده شوند. یک جریان پیشنهادی برای پروژههای حرفهای Laravel:
Route → Controller → Form Request → Service → Model/Transaction → Event → Listener/Job/Notification
مثال:
class OrderController extends Controller
{
public function store(StoreOrderRequest $request)
{
$order = $this->orderService->create(
$request->validated()
);
return redirect()->route('orders.show', $order);
}
}
Service:
class OrderService
{
public function create(array $data): Order
{
$order = DB::transaction(function () use ($data) {
return Order::create($data);
});
OrderCreated::dispatch($order);
return $order;
}
}
Listener:
class SendOrderCreatedSms
{
public function handle(OrderCreated $event): void
{
// Send SMS
}
}
در این ساختار:
- Controller درگیر ارسال پیامک و ایمیل نیست.
- Service مسئول فرآیند اصلی ثبت سفارش است.
- Event فقط اعلام میکند سفارش ایجاد شده است.
- Listenerها مسئول واکنشهای جانبی هستند.
Event و Queue؛ اجرای Listener در پسزمینه
گاهی Listener کار زمانبر انجام میدهد؛ مثل ارسال ایمیل، پیامک، اتصال به API خارجی، تولید گزارش یا همگامسازی با CRM. در این حالت بهتر است Listener در Queue اجرا شود تا Request اصلی کند نشود.
برای Queue کردن Listener کافی است Listener، Interface زیر را پیادهسازی کند:
Illuminate\Contracts\Queue\ShouldQueue
نمونه:
<?php
namespace App\Listeners;
use App\Events\OrderCreated;
use App\Services\NotificationService;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendOrderCreatedNotification implements ShouldQueue
{
public function __construct(
protected NotificationService $notificationService
) {}
public function handle(OrderCreated $event): void
{
$this->notificationService->sendOrderCreated($event->order);
}
}
حالا وقتی OrderCreated Dispatch شود، این Listener بهجای اجرای مستقیم، وارد Queue میشود و Worker آن را اجرا میکند.
در مستندات Laravel توضیح داده شده که اگر Listener پیادهسازی ShouldQueue داشته باشد، Event Dispatcher با استفاده از سیستم Queue لاراول آن Listener را به صف ارسال میکند. مطالعه مستندات رسمی Laravel درباره Queued Event Listeners
این قابلیت برای پروژههای واقعی بسیار مهم است؛ چون Eventها معمولاً چند Listener دارند و برخی از آنها ممکن است زمانبر باشند.
تنظیم Queue برای Listener
یک Listener صفی میتواند تنظیمات مختلفی داشته باشد.
تعیین Queue
public string $queue = 'notifications';
یا:
public function viaQueue(): string
{
return 'notifications';
}
تعیین Connection
public string $c;
یا:
public function viaConnection(): string
{
return 'redis';
}
تعداد تلاش
public int $tries = 3;
Timeout
public int $timeout = 60;
Backoff
public function backoff(): array
{
return [10, 60, 300];
}
نمونه کامل:
class SyncOrderWithCrm implements ShouldQueue
{
public string $c;
public string $queue = 'integrations';
public int $tries = 5;
public int $timeout = 120;
public function backoff(): array
{
return [30, 120, 600];
}
public function handle(OrderCreated $event): void
{
// Sync order with CRM
}
}
این ساختار برای APIهای خارجی بسیار مناسب است، چون ممکن است سرویس خارجی موقتاً قطع شود یا Rate Limit داشته باشد.
Event و Transaction دیتابیس
یکی از نکات بسیار مهم در استفاده از Eventها، رابطه آنها با Transaction دیتابیس است.
فرض کنید داخل Transaction یک سفارش ایجاد میکنید و همانجا Event را Dispatch میکنید:
DB::transaction(function () use ($data) {
$order = Order::create($data);
OrderCreated::dispatch($order);
});
اگر Listener شما Queue شود، ممکن است قبل از Commit شدن Transaction اجرا شود. در این حالت Listener ممکن است سفارشی را بخواند که هنوز در دیتابیس نهایی نشده یا حتی اگر Transaction بعداً Rollback شود، Listener برای سفارشی اجرا شده که واقعاً ثبت نشده است.
برای حل این مشکل، Laravel از اجرای Listenerهای صفی بعد از Commit پشتیبانی میکند. میتوانید در Listener مشخص کنید که بعد از Commit اجرا شود:
class SendOrderCreatedNotification implements ShouldQueue
{
public bool $afterCommit = true;
public function handle(OrderCreated $event): void
{
//
}
}
یا میتوانید Event را بعد از خروج موفق از Transaction منتشر کنید:
$order = DB::transaction(function () use ($data) {
return Order::create($data);
});
OrderCreated::dispatch($order);
در پروژههای مالی، سفارش، پرداخت و انبار، این نکته بسیار حیاتی است. ارسال ایمیل یا پیامک برای عملیاتی که هنوز Commit نشده، میتواند باعث ناهماهنگی جدی در تجربه کاربر و دادههای سیستم شود.
Event و Listener چه زمانی مناسب هستند؟
Eventها برای «رخدادهای مهم دامنه» مناسباند؛ یعنی اتفاقاتی که ممکن است بخشهای مختلف سیستم به آنها واکنش نشان دهند.
نمونههای مناسب:
| Event | Listenerهای احتمالی |
|---|---|
| UserRegistered | ارسال ایمیل خوشامدگویی، ثبت لاگ، ایجاد پروفایل اولیه |
| OrderCreated | ارسال پیامک، ایمیل، اطلاع به مدیر، همگامسازی CRM |
| PaymentSucceeded | فعالسازی سفارش، صدور فاکتور، ارسال رسید |
| InvoicePaid | تغییر وضعیت اشتراک، ارسال اعلان، ثبت سند حسابداری |
| PostPublished | پاکسازی Cache، تولید Sitemap، ارسال اعلان |
| TicketCreated | اطلاع به پشتیبان، ارسال ایمیل، ثبت Activity |
| SubscriptionExpired | غیرفعالسازی سرویس، ارسال یادآوری، ثبت لاگ |
Eventها زمانی عالی هستند که یک اتفاق اصلی رخ میدهد و چند واکنش جانبی ممکن است لازم باشد.
چه زمانی نباید از Event استفاده کنیم؟
Eventها مفید هستند، اما نباید برای هر چیز کوچکی استفاده شوند. استفاده بیش از حد از Event میتواند جریان برنامه را پنهان و Debug را سخت کند.
مواردی که شاید Event مناسب نباشد:
- وقتی فقط یک عملیات ساده و مستقیم دارید.
- وقتی ترتیب اجرای مراحل بسیار حساس و خطی است.
- وقتی خروجی Listener باید مستقیماً در همان لحظه برای ادامه عملیات استفاده شود.
- وقتی منطق اصلی دامنه را به Listenerهای پراکنده منتقل میکنید.
- وقتی Event فقط برای دور زدن طراحی بد Service استفاده میشود.
- وقتی بدون مستندسازی، Eventهای زیاد باعث گم شدن جریان برنامه میشوند.
مثلاً اگر ثبت سفارش باید حتماً ابتدا پرداخت را بررسی کند و نتیجه آن در ادامه همان عملیات استفاده شود، بهتر است این منطق اصلی داخل Service بماند، نه اینکه به Eventهای مبهم منتقل شود.
Event برای «واکنش به رخداد» مناسب است، نه برای پنهان کردن جریان اصلی کسبوکار.
تفاوت Event با Job در Laravel
Event و Job گاهی با هم اشتباه گرفته میشوند، اما هدف آنها متفاوت است.
| مورد | Event | Job |
|---|---|---|
| هدف | اعلام وقوع یک اتفاق | انجام یک کار مشخص |
| نامگذاری | گذشتهنگر و رخدادی | دستوری و عملیاتی |
| مثال | OrderCreated | SendOrderCreatedEmail |
| تعداد واکنش | میتواند چند Listener داشته باشد | معمولاً یک کار مشخص |
| وابستگی | منتشرکننده نباید Listenerها را بشناسد | Dispatch کننده معمولاً Job را میشناسد |
| Queue | Listener میتواند Queue شود | خود Job معمولاً Queue میشود |
اگر فقط میخواهید یک کار مشخص را در پسزمینه انجام دهید، Job کافی است:
SendInvoiceEmail::dispatch($invoice);
اما اگر یک اتفاق رخ داده و ممکن است چند بخش به آن واکنش نشان دهند، Event مناسبتر است:
InvoicePaid::dispatch($invoice);
سپس Listenerها:
SendInvoiceReceipt
ActivateSubscription
CreateAccountingEntry
NotifyAdmin
تفاوت Event با Observer در Laravel
Observerها بیشتر برای واکنش به رویدادهای Eloquent Model استفاده میشوند؛ مثل created، updated، deleted. اما Eventهای سفارشی برای رخدادهای دامنه و کسبوکار مناسبترند.
| مورد | Event سفارشی | Observer |
|---|---|---|
| سطح معنا | رخداد دامنه | رخداد مدل |
| مثال | OrderPaid | Order::updated |
| محل تمرکز | منطق کسبوکار | تغییرات Eloquent |
| خوانایی تجاری | بیشتر | کمتر |
| مناسب برای | فرآیندهای مهم سیستم | واکنش به تغییرات مدل |
| خطر | استفاده بیش از حد | پنهان شدن منطق پشت Model |
در مستندات رسمی Eloquent، بخشی برای Model Events وجود دارد و Laravel امکان واکنش به رویدادهایی مثل ایجاد، بهروزرسانی و حذف مدل را فراهم میکند. مطالعه مستندات رسمی Laravel درباره Eloquent Events
مثال Observer:
class ProductObserver
{
public function updated(Product $product): void
{
Cache::forget('products');
}
}
مثال Event سفارشی:
ProductPriceChanged::dispatch($product, $oldPrice, $newPrice);
اگر اتفاق شما معنی تجاری مشخص دارد، Event سفارشی معمولاً خواناتر است. اگر فقط میخواهید به تغییرات مدل واکنش نشان دهید، Observer انتخاب خوبی است.
Event Subscriber در Laravel
گاهی یک کلاس باید به چند Event گوش دهد. در این حالت میتوان از Event Subscriber استفاده کرد.
Subscriber یک کلاس است که چند متد Listener دارد و در یک متد subscribe مشخص میکند به چه Eventهایی گوش میدهد.
نمونه:
<?php
namespace App\Listeners;
use App\Events\OrderCreated;
use App\Events\OrderCancelled;
use Illuminate\Events\Dispatcher;
class OrderEventSubscriber
{
public function handleOrderCreated(OrderCreated $event): void
{
//
}
public function handleOrderCancelled(OrderCancelled $event): void
{
//
}
public function subscribe(Dispatcher $events): void
{
$events->listen(
OrderCreated::class,
[self::class, 'handleOrderCreated']
);
$events->listen(
OrderCancelled::class,
[self::class, 'handleOrderCancelled']
);
}
}
Subscriber برای زمانی مناسب است که چند رویداد مرتبط را در یک کلاس سازماندهی کنید؛ مثلاً رویدادهای سفارش یا پرداخت. اما نباید Subscriber را بیش از حد بزرگ کنید، چون ممکن است به یک کلاس سنگین و سختنگهداری تبدیل شود.
Event Broadcasting در Laravel
یکی از قابلیتهای قدرتمند Laravel، Event Broadcasting است. Broadcasting یعنی Eventهای سمت سرور را از طریق WebSocket یا سرویسهای مشابه به Frontend ارسال کنیم تا کاربران بدون Refresh صفحه تغییرات را ببینند.
طبق مستندات رسمی Laravel Broadcasting، این قابلیت اجازه میدهد Eventهای سمت سرور Laravel را از طریق اتصال WebSocket به کلاینت ارسال کنیم و همان نام Event و دادهها بین Backend و JavaScript Frontend به اشتراک گذاشته شود. مطالعه مستندات رسمی Laravel درباره Broadcasting
کاربردها:
- نمایش اعلان زنده
- چت آنلاین
- وضعیت سفارش در لحظه
- داشبورد مدیریتی Real-time
- نمایش تغییر وضعیت پرداخت
- اطلاعرسانی تیکت جدید
- نمایش Online بودن کاربران
- بهروزرسانی موجودی یا گزارشها
برای Broadcast کردن Event، معمولاً Event باید Interface زیر را پیادهسازی کند:
Illuminate\Contracts\Broadcasting\ShouldBroadcast
نمونه:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class OrderStatusUpdated implements ShouldBroadcast
{
public function __construct(
public Order $order
) {}
public function broadcastOn(): array
{
return [
new Channel('orders'),
];
}
}
در پروژههای مدرن، Broadcasting معمولاً با Laravel Echo، Reverb، Pusher یا Ably استفاده میشود. طبق مستندات رسمی Broadcasting در Laravel 12، Driverهای سرور شامل Laravel Reverb، Pusher Channels و Ably هستند. مطالعه مستندات رسمی Laravel درباره Broadcasting Drivers
Private Channel و Presence Channel
در Broadcasting، همه دادهها نباید عمومی باشند. برای همین Laravel از انواع Channel پشتیبانی میکند.
| نوع Channel | کاربرد |
|---|---|
| Public Channel | داده عمومی برای همه کاربران مجاز به اتصال |
| Private Channel | داده مخصوص یک کاربر یا منبع خاص |
| Presence Channel | مثل Private، اما با اطلاعات کاربران حاضر در کانال |
مثال Private Channel:
use Illuminate\Broadcasting\PrivateChannel;
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.' . $this->order->id),
];
}
برای این نوع Channel باید مجوز دسترسی تعریف شود تا فقط کاربران مجاز بتوانند رویداد را دریافت کنند.
مثال در routes/channels.php:
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return $user->orders()->whereKey($orderId)->exists();
});
این بخش در پروژههای واقعی بسیار مهم است؛ چون Broadcasting داده حساس بدون Authorization میتواند مشکل امنیتی ایجاد کند.
Event و Notification در Laravel
Eventها و Notificationها میتوانند کنار هم استفاده شوند. معمولاً Event اعلام میکند که اتفاقی رخ داده و Listener مسئول ارسال Notification است.
مثال:
OrderCreated::dispatch($order);
Listener:
class SendOrderCreatedNotification implements ShouldQueue
{
public function handle(OrderCreated $event): void
{
$event->order->user->notify(
new OrderCreatedNotification($event->order)
);
}
}
در این ساختار:
- Event فقط رخداد را اعلام میکند.
- Listener تصمیم میگیرد چه Notificationی ارسال شود.
- Notification مسئول کانالهای ارسال مثل mail، database، sms یا broadcast است.
این تفکیک برای پروژههای بزرگ بسیار تمیزتر است.
Event و Cache
یکی از کاربردهای رایج Eventها، پاکسازی یا بهروزرسانی Cache است. برای مثال، وقتی مقاله منتشر میشود یا محصول ویرایش میشود، Cacheهای مربوط به لیست محصولات یا صفحه اصلی باید پاک شوند.
Event:
PostPublished::dispatch($post);
Listener:
class ClearPostCache
{
public function handle(PostPublished $event): void
{
Cache::forget('latest_posts');
Cache::forget('post_' . $event->post->id);
}
}
این روش بهتر از آن است که پاکسازی Cache در چند Controller یا Service تکرار شود.
Event و سیستم لاگ فعالیت
در نرمافزارهای شرکتی، ثبت Activity Log اهمیت زیادی دارد. Eventها کمک میکنند لاگها بهشکل تمیز و جدا از منطق اصلی ثبت شوند.
مثال:
UserLoggedIn::dispatch($user);
OrderCreated::dispatch($order);
InvoicePaid::dispatch($invoice);
Listener:
class AddActivityLog
{
public function handle(OrderCreated $event): void
{
ActivityLog::create([
'user_id' => $event->order->user_id,
'action' => 'order_created',
'subject_type' => $event->order::class,
'subject_id' => $event->order->id,
]);
}
}
در سیستمهای CRM، ERP، حسابداری، پنل مدیریت و سامانههای سازمانی، این الگو بسیار کاربردی است.
Event و اتصال به سیستمهای خارجی
اگر بعد از یک رخداد مهم باید با سیستم خارجی ارتباط برقرار شود، Event و Listener ترکیب مناسبی هستند.
مثال:
CustomerCreated::dispatch($customer);
Listener:
class SyncCustomerWithCrm implements ShouldQueue
{
public int $tries = 5;
public function backoff(): array
{
return [30, 120, 600];
}
public function handle(CustomerCreated $event): void
{
// Call CRM API
}
}
چون API خارجی ممکن است کند یا موقتاً قطع باشد، بهتر است چنین Listenerهایی Queue شوند و Retry/Backoff مناسب داشته باشند.
تست Eventها در Laravel
Laravel امکانات خوبی برای تست Eventها دارد. میتوانید Eventها را Fake کنید و بررسی کنید آیا Event موردنظر Dispatch شده است یا نه.
use Illuminate\Support\Facades\Event;
use App\Events\OrderCreated;
Event::fake();
$this->post('/orders', $data);
Event::assertDispatched(OrderCreated::class);
بررسی شرطی:
Event::assertDispatched(OrderCreated::class, function ($event) use ($order) {
return $event->order->id === $order->id;
});
اگر میخواهید بعضی Eventها Fake شوند و بعضی اجرا شوند، میتوانید دقیقتر عمل کنید.
تست Listenerها نیز میتواند جداگانه انجام شود؛ مثلاً یک Event ساختگی به Listener بدهید و خروجی یا اثر آن را بررسی کنید.
$listener = app(SendOrderCreatedNotification::class);
$listener->handle(new OrderCreated($order));
در پروژههای حرفهای، تست Event Dispatch شدن و تست Listenerهای حساس، بهخصوص برای پرداخت، سفارش، اعلان و همگامسازی، بسیار مهم است.
Eventهای دامنهای در پروژههای شرکتی
در پروژههای شرکتی، بهتر است Eventها بر اساس زبان دامنه و اتفاقات مهم کسبوکار نامگذاری شوند، نه بر اساس جزئیات فنی.
نمونههای خوب:
UserRegistered
UserEmailVerified
OrderCreated
OrderCancelled
PaymentSucceeded
PaymentFailed
InvoiceIssued
InvoicePaid
SubscriptionStarted
SubscriptionExpired
TicketCreated
TicketReplied
PostPublished
ProductStockDepleted
این نامها برای مدیر محصول، تحلیلگر، برنامهنویس و تیم فنی قابل فهم هستند. وقتی Eventها زبان دامنه را بازتاب دهند، پروژه خواناتر و قابل نگهداریتر میشود.
ساختار پوشهبندی Eventها و Listenerها
برای پروژههای کوچک، ساختار پیشفرض کافی است:
app/
Events/
Listeners/
اما برای پروژههای بزرگتر، میتوان ساختار دامنهایتر داشت:
app/
Events/
Order/
OrderCreated.php
OrderCancelled.php
Payment/
PaymentSucceeded.php
PaymentFailed.php
User/
UserRegistered.php
Listeners/
Order/
SendOrderCreatedNotification.php
SyncOrderWithCrm.php
Payment/
ActivateOrderAfterPayment.php
SendPaymentReceipt.php
یا ساختار Domain-Based:
app/
Domain/
Order/
Events/
OrderCreated.php
Listeners/
SendOrderCreatedNotification.php
Payment/
Events/
PaymentSucceeded.php
Listeners/
ActivateSubscription.php
انتخاب ساختار بستگی به اندازه پروژه، تعداد تیم، پیچیدگی دامنه و سبک معماری دارد. برای شرکتهای نرمافزاری، ثبات در ساختار مهمتر از انتخاب یک الگوی خاص است.
جدول مقایسه Event با ابزارهای مشابه Laravel
| ابزار | هدف اصلی | زمان استفاده | مثال |
|---|---|---|---|
| Event | اعلام وقوع اتفاق | وقتی چند بخش باید به یک رخداد واکنش دهند | OrderCreated |
| Listener | واکنش به Event | ارسال ایمیل، پیامک، لاگ، Sync | SendOrderEmail |
| Job | اجرای یک کار مشخص، معمولاً در Queue | پردازش پسزمینه مستقیم | GenerateReportJob |
| Observer | واکنش به رویدادهای Model | created، updated، deleted | ProductObserver |
| Notification | ارسال پیام از کانالهای مختلف | mail، database، broadcast | OrderCreatedNotification |
| Service | اجرای منطق تجاری اصلی | ثبت سفارش، پرداخت، اشتراک | OrderService |
| Broadcasting | ارسال Event به Frontend در لحظه | داشبورد زنده، چت، اعلان | OrderStatusUpdated |
| Subscriber | گروهبندی Listenerهای مرتبط | چند Event مرتبط در یک کلاس | OrderEventSubscriber |
نمونه کامل: Event ثبت سفارش در Laravel
در این بخش یک نمونه کامل و حرفهای از Event ثبت سفارش را بررسی میکنیم.
ساخت Event
php artisan make:event OrderCreated
<?php
namespace App\Events\Order;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderCreated
{
use Dispatchable, SerializesModels;
public function __construct(
public Order $order
) {}
}
ساخت Listener ارسال اعلان
php artisan make:listener SendOrderCreatedNotification --event=OrderCreated
<?php
namespace App\Listeners\Order;
use App\Events\Order\OrderCreated;
use App\Services\NotificationService;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendOrderCreatedNotification implements ShouldQueue
{
public bool $afterCommit = true;
public string $queue = 'notifications';
public int $tries = 3;
public function __construct(
protected NotificationService $notificationService
) {}
public function handle(OrderCreated $event): void
{
$this->notificationService->sendOrderCreated($event->order);
}
}
ساخت Listener ثبت لاگ
<?php
namespace App\Listeners\Order;
use App\Events\Order\OrderCreated;
use App\Models\ActivityLog;
class AddOrderCreatedActivityLog
{
public function handle(OrderCreated $event): void
{
ActivityLog::create([
'user_id' => $event->order->user_id,
'action' => 'order_created',
'subject_type' => $event->order::class,
'subject_id' => $event->order->id,
]);
}
}
Dispatch در Service
<?php
namespace App\Services;
use App\Events\Order\OrderCreated;
use App\Models\Order;
use Illuminate\Support\Facades\DB;
class OrderService
{
public function create(array $data): Order
{
$order = DB::transaction(function () use ($data) {
return Order::create([
'user_id' => $data['user_id'],
'status' => 'created',
'total_price' => $data['total_price'],
]);
});
OrderCreated::dispatch($order);
return $order;
}
}
در این ساختار، ثبت سفارش مستقل از ارسال اعلان و ثبت لاگ است. اگر بعداً بخواهید Listener جدیدی برای همگامسازی CRM اضافه کنید، نیازی به تغییر OrderService ندارید.
Event Broadcasting نمونه کاربردی
فرض کنید در پنل کاربر میخواهید وضعیت سفارش بهصورت زنده تغییر کند. میتوانید Eventی مثل OrderStatusUpdated بسازید.
<?php
namespace App\Events\Order;
use App\Models\Order;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class OrderStatusUpdated implements ShouldBroadcast
{
public function __construct(
public Order $order
) {}
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.' . $this->order->id),
];
}
public function broadcastAs(): string
{
return 'order.status.updated';
}
}
تعریف مجوز Channel:
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return $user->orders()->whereKey($orderId)->exists();
});
این روش برای پنلهای Real-time، سیستم تیکتینگ، داشبورد ادمین، اعلانهای زنده و اپلیکیشنهای SPA بسیار کاربردی است.
اشتباهات رایج در استفاده از Event در Laravel
1. نامگذاری اشتباه Event
Event باید اتفاق را توصیف کند، نه کاری که باید انجام شود.
نام بد:
SendWelcomeEmail
نام خوب:
UserRegistered
2. انتقال منطق اصلی کسبوکار به Listener
Listener برای واکنش جانبی است، نه اجرای هسته اصلی فرآیند. منطق اصلی باید در Service یا Domain Layer بماند.
3. Queue نکردن Listenerهای زمانبر
ارسال ایمیل، پیامک، API خارجی و گزارشگیری بهتر است Queue شوند.
4. Dispatch کردن Event داخل Transaction بدون توجه به Commit
اگر Listener به داده Commitشده نیاز دارد، Event را بعد از Transaction منتشر کنید یا از afterCommit استفاده کنید.
5. ساخت Event برای هر کار کوچک
Event زیاد و بیهدف باعث پیچیدگی و سختی Debug میشود.
6. وابسته کردن Event به جزئیات UI
Eventهای Backend بهتر است رخداد دامنه را بیان کنند، نه رفتار خاص یک View یا صفحه.
7. قرار دادن داده حساس زیاد داخل Event
Eventها، مخصوصاً اگر Queue یا Broadcast شوند، نباید داده حساس غیرضروری حمل کنند.
8. نداشتن تست برای Eventهای مهم
برای رخدادهای مهم مثل پرداخت، سفارش، اشتراک و اعلان، تست Event و Listener ضروری است.
9. استفاده نامناسب از Broadcasting
هر Eventی نباید Broadcast شود. فقط دادههایی را Real-time ارسال کنید که واقعاً برای Frontend لازم است.
10. نبود مستندات رویدادها
در پروژههای بزرگ، باید مشخص باشد چه Eventهایی وجود دارند و چه Listenerهایی به آنها متصل هستند.
بهترین روشهای استفاده از Event در Laravel
1. Eventها را بر اساس زبان دامنه نامگذاری کنید
از نامهایی مثل OrderCreated، PaymentSucceeded و PostPublished استفاده کنید.
2. Event را ساده نگه دارید
Event باید داده لازم را حمل کند، نه اینکه خودش منطق سنگین اجرا کند.
3. Listenerها را کوچک و متمرکز طراحی کنید
هر Listener بهتر است یک مسئولیت مشخص داشته باشد.
4. Listenerهای زمانبر را Queue کنید
ارسال ایمیل، پیامک، API خارجی و Sync بهتر است با ShouldQueue انجام شود.
5. به Transaction و afterCommit توجه کنید
در عملیات دیتابیسی حساس، Event را بعد از Commit منتشر کنید یا Listener را بعد از Commit اجرا کنید.
6. از Service داخل Listener استفاده کنید
Listener بهتر است هماهنگکننده باشد و منطق جدی را به Service بسپارد.
7. Eventهای مهم را تست کنید
با Event::fake() بررسی کنید Eventهای کلیدی Dispatch شدهاند.
8. Broadcasting را امن طراحی کنید
برای دادههای خصوصی از Private Channel و Authorization استفاده کنید.
9. Listenerهای خارجی را مقاوم کنید
برای APIهای خارجی Retry، Backoff و Queue مناسب تعریف کنید.
10. نقشه Eventها را مستند کنید
در پروژههای شرکتی، مستندسازی Eventها و Listenerها باعث کاهش ابهام تیمی میشود.
Eventها و سئو در پروژههای Laravel
Eventها بهصورت مستقیم ابزار SEO نیستند، اما در سایتهای شرکتی، فروشگاهی و محتوایی میتوانند کیفیت فنی و تجربه کاربری را بهبود دهند.
برای مثال، وقتی مقالهای منتشر میشود:
PostPublished::dispatch($post);
Listenerهای احتمالی:
- پاکسازی Cache صفحه اصلی
- تولید مجدد Sitemap
- ارسال اعلان به مشترکان
- ثبت Activity Log
- ارسال Ping به سرویسهای داخلی
- بهروزرسانی دادههای جستجوی داخلی
- ارسال رویداد به سیستم تحلیل محتوا
نمونه:
class UpdateSitemapAfterPostPublished implements ShouldQueue
{
public function handle(PostPublished $event): void
{
// Regenerate sitemap or notify sitemap service
}
}
در فروشگاه اینترنتی نیز Eventهایی مثل ProductUpdated یا CategoryUpdated میتوانند برای پاکسازی Cache، بهروزرسانی صفحههای لیست محصول و حفظ سرعت سایت استفاده شوند. سرعت بهتر و دادههای بهروزتر، به تجربه کاربری و عملکرد سئو کمک میکند.
FAQ؛ سوالات متداول درباره Event در Laravel
1. Event در Laravel چیست؟
Event در Laravel کلاسی است که وقوع یک اتفاق مهم در برنامه را اعلام میکند؛ مثل ثبت سفارش، پرداخت موفق یا ثبتنام کاربر. Listenerها به این Event گوش میدهند و واکنش مناسب انجام میدهند.
2. Listener در Laravel چیست؟
Listener کلاسی است که هنگام Dispatch شدن یک Event اجرا میشود. برای مثال، Listener میتواند بعد از ثبت سفارش ایمیل ارسال کند، پیامک بفرستد یا لاگ ثبت کند.
3. تفاوت Event و Job چیست؟
Event وقوع یک اتفاق را اعلام میکند و ممکن است چند Listener داشته باشد. Job یک کار مشخص را انجام میدهد و معمولاً برای پردازش پسزمینه استفاده میشود.
4. تفاوت Event و Observer چیست؟
Observer بیشتر برای واکنش به رویدادهای Eloquent Model مثل created و updated استفاده میشود. Event سفارشی برای رخدادهای دامنه و کسبوکار مثل OrderPaid یا UserRegistered مناسبتر است.
5. چطور در Laravel یک Event بسازیم؟
با دستور Artisan زیر:
php artisan make:event OrderCreated
سپس میتوانید Event را با OrderCreated::dispatch($order) منتشر کنید.
6. چطور Listener بسازیم؟
با دستور زیر:
php artisan make:listener SendOrderCreatedNotification --event=OrderCreated
Listener باید متد handle داشته باشد و Event مربوطه را دریافت کند.
7. آیا Listenerها میتوانند Queue شوند؟
بله. اگر Listener، Interface ShouldQueue را پیادهسازی کند، بهصورت Queue اجرا میشود و Worker آن را در پسزمینه پردازش میکند.
8. Event Discovery چیست؟
Event Discovery قابلیتی است که Laravel با اسکن دایرکتوری Listenerها، Listenerهای مربوط به Eventها را بهصورت خودکار شناسایی و ثبت میکند.
9. Event Broadcasting چیست؟
Broadcasting یعنی ارسال Eventهای سمت سرور به Frontend از طریق WebSocket یا سرویسهای مشابه تا کاربران تغییرات را در لحظه ببینند. این قابلیت برای چت، اعلان زنده و داشبوردهای Real-time کاربرد دارد.
10. آیا باید همه کارهای جانبی را با Event انجام دهیم؟
نه همیشه. Event زمانی مناسب است که یک رخداد مهم رخ داده و چند بخش ممکن است به آن واکنش نشان دهند. برای یک کار ساده و مستقیم، Job یا Service ممکن است کافی باشد.
11. آیا Event باید منطق تجاری داشته باشد؟
معمولاً خیر. Event باید ساده باشد و فقط داده مربوط به رخداد را حمل کند. منطق تجاری باید در Service یا Listenerهای مشخص قرار بگیرد.
12. آیا Eventها برای پروژههای کوچک هم مفید هستند؟
اگر پروژه کوچک است و منطق سادهای دارد، شاید Event ضروری نباشد. اما اگر پروژه در حال رشد است یا چند واکنش به رخدادهای مهم دارید، Eventها از ابتدا میتوانند معماری را تمیزتر کنند.
جمعبندی
Event در Laravel یکی از ابزارهای مهم برای طراحی نرمافزارهای تمیز، منعطف و توسعهپذیر است. با استفاده از Event و Listener میتوان منطق اصلی برنامه را از واکنشهای جانبی جدا کرد. بهجای اینکه بعد از ثبت سفارش، ارسال ایمیل، پیامک، لاگ، پاکسازی Cache و همگامسازی CRM همگی داخل Service یا Controller نوشته شوند، یک Event مثل OrderCreated منتشر میشود و Listenerهای جداگانه هرکدام مسئولیت مشخص خود را انجام میدهند.
این الگو باعث کاهش وابستگی، افزایش خوانایی، تستپذیری بهتر و آمادگی بیشتر پروژه برای رشد میشود. Eventها در کنار Queue، Notification، Broadcasting، Service Layer و Observer میتوانند معماری Laravel را حرفهایتر کنند. با این حال، استفاده از Event باید هدفمند باشد. Event نباید جای منطق اصلی کسبوکار را بگیرد و نباید برای هر عملیات کوچک Event ساخته شود.
در پروژههای شرکتی، Eventها برای رخدادهایی مثل ثبت سفارش، پرداخت موفق، صدور فاکتور، ثبتنام کاربر، تغییر وضعیت تیکت، انتشار مقاله و همگامسازی با سیستمهای خارجی بسیار کاربردی هستند. اگر Listenerها زمانبر باشند، باید Queue شوند. اگر Event داخل Transaction استفاده میشود، باید به Commit شدن داده توجه شود. اگر Event به Frontend ارسال میشود، باید Broadcasting با Channelهای امن و مجوزدهی مناسب طراحی شود.
تسلط بر Event در Laravel به تیمهای نرمافزاری کمک میکند پروژههایی بسازند که هم تمیزتر هستند، هم بهتر تست میشوند، هم برای توسعه قابلیتهای جدید آمادهترند. ⚡
CTA
اگر پروژه Laravel شما پر از منطقهای جانبی داخل Controller یا Service شده و اضافه کردن قابلیتهای جدید باعث پیچیدگی بیشتر کد میشود، زمان آن رسیده معماری Event-Driven را جدیتر بررسی کنید. تیم ما میتواند در طراحی Event و Listener، Queue کردن پردازشها، پیادهسازی Notification، راهاندازی Broadcasting، بازطراحی Service Layer و استانداردسازی معماری Laravel به شما کمک کند. برای بررسی فنی پروژه خود با ما تماس بگیرید و ساختار نرمافزار را تمیزتر، مقیاسپذیرتر و حرفهایتر کنید. 🚀
منابع رسمی
- مستندات رسمی Laravel درباره Events؛ برای آشنایی با Event، Listener، Event Discovery، Queued Listener و Event Subscriber.
مطالعه مستندات رسمی Laravel درباره Events - مستندات رسمی Laravel درباره Queued Event Listeners؛ برای اجرای Listenerها در Queue، مدیریت تلاش مجدد و اجرای پسزمینه.
مطالعه مستندات رسمی Laravel درباره Queued Event Listeners - مستندات رسمی Laravel درباره Event Broadcasting؛ برای ارسال Eventهای سمت سرور به Frontend از طریق WebSocket و استفاده در داشبوردهای Real-time.
مطالعه مستندات رسمی Laravel درباره Broadcasting - مستندات رسمی Laravel درباره Eloquent Events؛ برای آشنایی با رویدادهای مدل و تفاوت آنها با Eventهای سفارشی دامنه.
مطالعه مستندات رسمی Laravel درباره Eloquent Events