Laravel 数据库通知聚合与计数更新策略

聖光之護
发布: 2025-11-24 12:44:13
原创
954人浏览过

laravel 数据库通知聚合与计数更新策略

本文旨在提供一种优化 Laravel 数据库通知的方法,以避免在短时间内发送大量重复或相似的通知。通过实现基于时间窗口的通知聚合策略,可以在特定时间段内将多个相关事件合并为一个通知,并自动更新其计数,从而提升用户体验并减少数据库负担。

在构建现代 Web 应用程序时,通知功能是用户体验的关键组成部分。然而,当系统在短时间内触发大量相似的通知时(例如,多个新帖子匹配了用户的保存搜索),用户可能会感到信息过载。为了解决这一问题,我们可以采用一种策略:在指定的时间窗口内,将相关的通知进行聚合,而不是为每个事件都创建一条新的通知。这种聚合方式通常表现为更新现有通知的计数和内容。

核心策略:基于时间窗口的通知聚合

该策略的核心思想是:当一个新事件触发通知时,首先检查在最近一个预定义的时间窗口内(例如30分钟),是否存在与此事件高度相关的未读通知。

  1. 如果存在:更新该现有通知的计数和内容,并阻止系统创建新的通知记录。
  2. 如果不存在:则创建一个全新的通知。

通过这种方式,用户只会看到一条不断更新的通知,而不是一连串独立的通知,直到超过时间窗口或用户阅读了该通知。

AI帮个忙
AI帮个忙

多功能AI小工具,帮你快速生成周报、日报、邮、简历等

AI帮个忙 116
查看详情 AI帮个忙

实现方法:修改 toDatabase 方法

Laravel 的通知系统通过 toDatabase 方法定义了通知如何存储到数据库。为了实现通知聚合,我们需要在该方法中加入条件逻辑。关键在于,当成功更新现有通知时,toDatabase 方法应返回 null 或 false,以阻止 Laravel 默认的通知创建行为。

以下是修改后的 toDatabase 方法示例,它实现了基于搜索 ID 和时间窗口的通知聚合:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\DatabaseMessage; // 引入 DatabaseMessage 可选,直接返回数组也可

class PostMatchedSearch extends Notification implements ShouldQueue // 推荐使用队列
{
    use Queueable;

    protected $search;
    // 可以在构造函数中传入更多上下文信息,例如触发通知的 Post 对象
    protected $post; 

    /**
     * 创建一个新的通知实例。
     *
     * @param  \App\Models\Search  $search
     * @param  \App\Models\Post|null  $post
     * @return void
     */
    public function __construct($search, $post = null)
    {
        $this->search = $search;
        $this->post = $post;
    }

    /**
     * 获取通知的投递渠道。
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable): array
    {
        return ['database'];
    }

    /**
     * 获取通知的数据库表示。
     *
     * @param  mixed  $notifiable
     * @return array|null
     */
    public function toDatabase($notifiable): ?array
    {
        // 定义通知聚合的时间窗口(例如30分钟)
        $timeWindowMinutes = 30;

        // 构造当前通知的数据,其中包含用于识别聚合的键(如 'search_id')
        $currentNotificationData = [
            'search_id' => $this->search->id,
            'title' => $this->search->title,
            'parameters' => $this->search->parameters,
            // 如果需要,可以在这里记录触发当前通知的最新帖子ID
            'latest_post_id' => $this->post ? $this->post->id : null, 
        ];

        // 尝试查找在指定时间窗口内,与当前搜索相关的未读通知
        // 1. where('type', self::class) 确保只聚合同类型的通知
        // 2. where('read_at', null) 确保只聚合用户尚未阅读的通知
        // 3. where('data->search_id', $this->search->id) 通过JSON字段精确匹配搜索ID
        // 4. where('created_at', '>=', now()->subMinutes($timeWindowMinutes)) 定义时间窗口
        $existingNotification = $notifiable->notifications()
            ->where('type', self::class)
            ->where('read_at', null) 
            ->where('data->search_id', $this->search->id) 
            ->where('created_at', '>=', now()->subMinutes($timeWindowMinutes))
            ->first();

        if ($existingNotification) {
            // 如果找到现有通知,更新其计数和内容
            $currentCount = $existingNotification->data['count'] ?? 1;
            $newCount = $currentCount + 1;

            // 更新现有通知的数据
            $existingNotification->update([
                'data' => array_merge($existingNotification->data, [
                    'count' => $newCount,
                    'content' => [
                        'en' => "{$newCount} new posts matched with your saved search {$this->search->title} have been posted. Click here to view more.",
                        // 可以添加一个时间戳,表示最后更新时间
                        'last_updated' => now()->toDateTimeString(),
                    ],
                    // 更新最新的帖子ID
                    'latest_post_id' => $this->post ? $this->post->id : null,
                ]),
                'updated_at' => now(), // 手动更新 updated_at 字段
            ]);

            // 返回 null 阻止创建新的通知记录
            return null;
        }

        // 如果没有找到现有通知,则创建新的通知
        return [
            'content' => [
                'en' => "1 new post matched with your saved search {$this->search->title} has been posted. Click here to view more.",
            ],
            'count' => 1,
            // 确保这里返回的数据结构与查询时使用的键(如 'search_id')一致
            'search_id' => $this->search->id, 
            'parameters' => $this->search->parameters,
            'latest_post_id' => $this->post ? $this->post->id : null,
        ];
    }
}
登录后复制

注意事项

  1. return null 的关键作用:当 toDatabase 方法返回 null 或 false 时,Laravel 的通知系统将不会在 notifications 表中创建新的记录。这是实现通知聚合而不创建新通知的核心机制。
  2. 通知类型过滤 (where('type', self::class)):在查询现有通知时,务必通过 where('type', self::class) 过滤通知类型。这确保了我们只聚合相同类型的通知,避免将不同目的的通知混淆。
  3. read_at 状态过滤 (where('read_at', null)):通常情况下,我们只希望聚合用户尚未阅读的通知。一旦用户阅读了通知,即使在时间窗口内,也应该创建一个新的通知,以确保用户不会错过新的信息。
  4. 数据结构设计与查询
    • 在 toDatabase 返回的数组中,应包含一个或多个用于唯一标识聚合通知的键(例如 search_id)。
    • 查询现有通知时,使用 where('data-youjiankuohaophpcnyour_key', $value) 来精确匹配 JSON 字段中的特定键值。确保存储的键名和查询的键名一致。
  5. 手动更新 updated_at:当通过 update() 方法修改现有通知记录时,Laravel 默认不会自动更新 updated_at 字段(因为 MorphMany 关系通常不直接触发模型事件)。因此,建议手动将 updated_at 设置为 now(),以反映通知的实际更新时间。
  6. 内容动态更新:确保通知内容(如 content 字段)能够动态反映最新的计数和状态。在示例中,我们将计数直接嵌入到通知文本中。
  7. 队列处理 (ShouldQueue):对于可能涉及数据库查询和更新的通知逻辑,强烈建议实现 ShouldQueue 接口,将通知的发送和处理推送到队列中,以避免阻塞用户请求。
  8. 聚合粒度:根据业务需求,聚合的粒度可以更细致。例如,除了 search_id,还可以考虑用户的其他偏好设置或更复杂的事件关联规则。

总结

通过在 toDatabase 方法中引入条件逻辑并巧妙利用 return null,我们能够有效地实现 Laravel 数据库通知的聚合与计数更新。这种策略不仅减少了通知的泛滥,提升了用户体验,也降低了数据库的存储压力。在设计通知系统时,灵活运用这种模式,可以根据实际业务场景创建更加智能和用户友好的通知机制。

以上就是Laravel 数据库通知聚合与计数更新策略的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号