Laravel 8中能否通过单个事件同时给用户和管理员发不同邮件?
Laravel 8 精简请假系统邮件通知的Event/Listener实现
需求背景
开发企业员工请假申请系统时,提交申请、审批/驳回后需要给员工和管理员发送不同内容的邮件通知。目前用4组Event/Listener实现,仅邮件内容和模板不同,希望减少重复的Event/Listener数量。
解决方案:通用事件+通用监听器+通用邮件类
通过单一通用事件传递通知类型、收件人、请假请求数据,配合单一监听器和通用邮件类,根据类型自动选择对应的邮件模板和主题,实现用最少的Event/Listener覆盖所有通知场景。
步骤1:创建通用LeaveNotificationEvent
这个事件包含所有通知需要的核心数据:收件人、请假请求、通知类型。
<?php namespace App\Events; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; use App\Models\User; use App\Models\LeaveRequest; class LeaveNotificationEvent { use Dispatchable, SerializesModels; public $recipient; public $leaveRequest; public $type; /** * 通知类型枚举: * - request_submitted_admin: 提交申请通知管理员 * - request_submitted_user: 提交申请通知申请人 * - request_approved_admin: 审批通过通知管理员 * - request_approved_user: 审批通过通知申请人 * - request_rejected_admin: 驳回申请通知管理员 * - request_rejected_user: 驳回申请通知申请人 */ public function __construct(User $recipient, LeaveRequest $leaveRequest, string $type) { $this->recipient = $recipient; $this->leaveRequest = $leaveRequest; $this->type = $type; $this->recipient->locale = app()->currentLocale(); } public function broadcastOn() { return new PrivateChannel('channel-name'); } }
步骤2:创建通用LeaveNotificationListener
监听器只负责调用邮件发送逻辑,无需关心具体邮件内容,所有差异化逻辑交给邮件类处理。
<?php namespace App\Listeners; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Support\Facades\Mail; use App\Mail\GenericLeaveNotification; use App\Events\LeaveNotificationEvent; class LeaveNotificationListener { public function __construct() { // } public function handle(LeaveNotificationEvent $event) { Mail::to($event->recipient->email) ->locale($event->recipient->locale) ->send(new GenericLeaveNotification( $event->recipient, $event->leaveRequest, $event->type )); } }
步骤3:创建通用GenericLeaveNotification邮件类
邮件类根据通知类型,动态加载对应的Blade模板和邮件主题,原有模板可直接复用。
<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\URL; use App\Models\User; use App\Models\LeaveRequest; class GenericLeaveNotification extends Mailable { use Queueable, SerializesModels; public $recipient; public $leaveRequest; public $type; public function __construct(User $recipient, LeaveRequest $leaveRequest, string $type) { $this->recipient = $recipient; $this->leaveRequest = $leaveRequest; $this->type = $type; } public function build() { $config = $this->getNotificationConfig(); return $this->markdown($config['template'], [ 'url' => URL::to('/home'), 'recipient' => $this->recipient, 'leaveRequest' => $this->leaveRequest, ])->subject($config['subject']); } private function getNotificationConfig(): array { return match($this->type) { 'request_submitted_admin' => [ 'template' => 'email.leaverequestadmin', 'subject' => 'Incoming Leave Request' ], 'request_submitted_user' => [ 'template' => 'email.leaverequestuser', 'subject' => 'Leave Request Received' ], 'request_approved_admin' => [ 'template' => 'email.leaveapprovedadmin', 'subject' => 'Leave Request Approved' ], 'request_approved_user' => [ 'template' => 'email.leaveapproveduser', 'subject' => 'Your Leave Request Has Been Approved' ], 'request_rejected_admin' => [ 'template' => 'email.leaverejectedadmin', 'subject' => 'Leave Request Rejected' ], 'request_rejected_user' => [ 'template' => 'email.leaverejecteduser', 'subject' => 'Your Leave Request Has Been Rejected' ], default => throw new \InvalidArgumentException('Invalid notification type') }; } }
步骤4:更新控制器中的事件触发逻辑
替换原有多个事件触发代码,统一使用LeaveNotificationEvent并传递对应类型。
提交申请场景(store方法)
// 保存请假申请后 if($leave_request->save()){ $user = auth()->user(); $assignee = User::find($request->assignee); // 通知审批人 event(new LeaveNotificationEvent($assignee, $leave_request, 'request_submitted_admin')); // 通知复核人(如果存在) if($request->reviewer != null){ $reviewer = User::find($request->reviewer); event(new LeaveNotificationEvent($reviewer, $leave_request, 'request_submitted_admin')); } // 通知申请人 event(new LeaveNotificationEvent($user, $leave_request, 'request_submitted_user')); DB::commit(); return response()->json([ 'message'=> 'Leave requested successfully', 'status_code' => 200, ]); }
审批/驳回场景示例
// 审批通过时 public function approve(Request $request, LeaveRequest $leaveRequest) { // 执行审批逻辑... $leaveRequest->update(['status' => 'approved']); $approver = auth()->user(); $applicant = User::find($leaveRequest->requested_by); // 通知审批管理员 event(new LeaveNotificationEvent($approver, $leaveRequest, 'request_approved_admin')); // 通知申请人 event(new LeaveNotificationEvent($applicant, $leaveRequest, 'request_approved_user')); return response()->json(['message' => 'Leave request approved']); } // 驳回申请时 public function reject(Request $request, LeaveRequest $leaveRequest) { // 执行驳回逻辑... $leaveRequest->update(['status' => 'rejected']); $approver = auth()->user(); $applicant = User::find($leaveRequest->requested_by); // 通知审批管理员 event(new LeaveNotificationEvent($approver, $leaveRequest, 'request_rejected_admin')); // 通知申请人 event(new LeaveNotificationEvent($applicant, $leaveRequest, 'request_rejected_user')); return response()->json(['message' => 'Leave request rejected']); }
步骤5:注册事件与监听器
在app/Providers/EventServiceProvider.php中更新事件映射:
protected $listen = [ LeaveNotificationEvent::class => [ LeaveNotificationListener::class, ], ];
优势
- 仅需1个Event和1个Listener,彻底消除重复代码
- 通知类型枚举清晰,便于维护和扩展新场景
- 原有Blade模板无需修改,直接复用
- 逻辑集中在邮件类,代码结构更简洁
内容的提问来源于stack exchange,提问作者Tulsi Acharya
相关产品推荐
相关产品推荐

