Laravel如何检测Notification通知是否已成功分发
Laravel Notification 发送结果判定方案
问题本质
你遇到的报错:
'void' method 'notify' result used
核心原因是Laravel自带的notify()方法本身设计为无返回值(void类型),直接拿它的返回值做if判断永远不会得到预期结果,原代码里mark_as_delivered逻辑永远不执行就是这个原因。
通知发送成功/失败的判定逻辑和你用的发送驱动(同步/队列)直接相关:
- 同步发送:
notify()执行过程不抛异常就代表发送成功,抛出异常就是发送失败 - 异步队列发送:
notify()执行只代表任务成功入队,不代表邮件实际发出去了,最终发送失败的任务会自动写入failed_jobs表
具体实现代码
同步发送场景
直接用try/catch捕获发送过程的异常即可,无异常即为发送成功:
<?php class SendCertificates { public function __invoke() { // 补全原代码缺失的证书实例获取逻辑,根据你的业务场景调整取数方式 $certificate = Certificate::query()->find(/* 对应证书ID */); if ($this->send_certificate($certificate)) { $this->mark_as_delivered($certificate); } else { // 自定义失败逻辑:记日志、标记证书状态为发送失败、触发告警都可以 } } public function send_certificate(Certificate $certificate): bool { try { Notification::route('mail', [ $certificate->participant->email => $certificate->participant->name, ])->notify(new CertificateIssued($certificate)); // 走到这行没有抛出异常,就代表发送成功 return true; } catch (\Throwable $e) { // 按需记录错误详情 \Log::error('证书通知发送失败', [ 'certificate_id' => $certificate->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return false; } } }
异步队列发送场景
如果你的CertificateIssued通知实现了ShouldQueue接口走异步队列,不要在调用notify()后立刻更新送达状态,此时邮件还没实际发送。可以选以下两种方案判定结果:
- 方案1:监听队列事件自动更新状态
在AppServiceProvider的boot方法中注册队列失败、成功事件回调,自动更新证书状态:use Illuminate\Queue\Events\JobFailed; use Illuminate\Queue\Events\JobProcessed; use Illuminate\Support\Facades\Queue; use App\Notifications\CertificateIssued; use App\Models\Certificate; public function boot() { // 队列任务执行成功:标记证书已送达 Queue::after(function (JobProcessed $event) { if ($event->job->resolveName() === CertificateIssued::class) { $command = unserialize($event->job->payload()['data']['command']); Certificate::where('id', $command->certificate->id)->update(['delivered' => true]); } }); // 队列任务执行失败:标记证书发送失败 Queue::failing(function (JobFailed $event) { if ($event->job->resolveName() === CertificateIssued::class) { $command = unserialize($event->job->payload()['data']['command']); Certificate::where('id', $command->certificate->id)->update([ 'delivered' => false, 'error_msg' => $event->exception->getMessage() ]); } }); } - 方案2:定期查询
failed_jobs表对账
写定时任务扫描failed_jobs表,解析失败任务的payload内容,匹配到证书发送任务后更新对应证书的失败状态;已经从队列中移除、且不在失败表中的任务,即为发送成功。
注意事项
- 不要尝试直接获取
notify()的返回值做判断,该方法本身无返回值,所有成功失败判定都基于「是否抛出异常」的逻辑 - 队列场景下必须等任务实际执行完成再更新状态,入队成功不代表发送成功
- 原代码
__invoke方法里直接使用$certificate变量没有提前实例化,本身就会触发变量未定义错误,需要按业务逻辑补全取数步骤
内容的提问来源于stack exchange,提问作者Pathros
相关产品推荐
相关产品推荐

