You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:15:55