用Laravel建立一個通知系統

建立一個系統來發送通知給用戶,以下內容是經多次改版出來,只為自己筆記為主,順便分享,當然未來如果做了簡化工作,我會再來更新

假設系統名為 Notic Model 來保存所有記錄 我故意不使用Notificationt這個字, 因為這個名稱Laravel內部已使用,甚至我整個流程中也會用到Notification來協助我們寄出通知,因為我們的SystemNotic是 extends Notification

Model Notic.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Notic extends Model
{
    use SoftDeletes;

    public $incrementing = false;  // 禁用自增
    protected $keyType = 'string'; // 設置主鍵類型為字符串

    protected $table = 'notifications'; // 使用 Laravel 默認表名

    protected $fillable = [
        'id',           // UUID
        'type',         // 通知類型
        'title',        // 通知標題
        'content',      // 通知內容
        'notifiable_type',  // 默認會是 'App\Models\User'
        'notifiable_id',    // 用戶 ID
        'data',         // JSON 數據,可以包含我們的自定義字段
        'read_at',      // 已讀時間
        'created_at',
        'updated_at'
    ];

    protected $casts = [
        'data' => 'array',
        'read_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    // 我們可以通過 data 字段存儲額外信息
    public function getTitleAttribute()
    {
        return $this->data['title'] ?? '';
    }

    public function getContentAttribute()
    {
        return $this->data['message'] ?? '';
    }

    public function getTypeAttribute()
    {
        return $this->data['type'] ?? 'info';
    }

    public function user()
    {
        return $this->belongsTo(User::class, 'notifiable_id');
    }
} 

建立一個通知 app/Notifications/SystemNotic.php

這是extends Notification的!

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;

class SystemNotic extends Notification
{
    protected $title;
    protected $message;
    protected $type;
    protected $data;

    public function __construct($title, $message, $type = 'info', $data = [])
    {
        $this->title = $title;
        $this->message = $message;
        $this->type = $type;
        $this->data = $data;
    }

    public function via($notifiable)
    {
        return ['database'];
    }

    public function toDatabase($notifiable)
    {
        return [
            'title' => $this->title,
            'message' => $this->message,
            'type' => $this->type,
            'data' => array_merge($this->data, [
                'title' => $this->title,
                'message' => $this->message,
                'type' => $this->type
            ])
        ];
    }
} 

建立一個 Service app/Services/NoticService.php

<?php

namespace App\Services;

use App\Models\User;
use App\Notifications\SystemNotic;
use Illuminate\Support\Facades\Notification;

class NoticService
{
    /**
     * 發送通知給指定用戶
     */
    public function sendToUser($userId, $title, $message, $type = 'info', $data = [])
    {
        $user = User::find($userId);
        if ($user) {
            $user->notify(new SystemNotic($title, $message, $type, $data));
        }
    }

    /**
     * 發送通知給多個用戶
     */
    public function sendToUsers($userIds, $title, $message, $type = 'info', $data = [])
    {
        $users = User::whereIn('id', $userIds)->get();
        Notification::send($users, new SystemNotic($title, $message, $type, $data));
    }

    /**
     * 發送通知給所有用戶
     */
    public function sendToAll($title, $message, $type = 'info', $data = [])
    {
        $users = User::all();
        Notification::send($users, new SystemNotic($title, $message, $type, $data));
    }
} 

建一個 Traits app/Traits/HasNotics.php

<?php

namespace App\Traits;

use App\Services\NoticService;

trait HasNotics
{
    public function notic()
    {
        return app(NoticService::class);
    }
} 

使用方法

發送通知

// 方法 1: 使用 NoticService
$noticService->sendToUser(
        auth()->id(),
        '通知標題',
        '通知內容',
        'info', // 通知類型: info, success, warning, danger
        ['action' => 'view_task'] // 額外數據
);

// 發送給單個用戶
$noticService->sendToUser(1, '任務列表已更新', '您有新的任務');

// 發送給多個用戶
$noticService->sendToUsers([1, 2, 3], '任務列表已更新', '有新的任務');

// 方法 2: 使用 HasNotics Trait
$model->notic()->sendToUser(
    auth()->id(),
    '通知標題',
    '通知內容'
);

推薦使用 Trait 方法2,方便在模型中使用

Demo:當修改客戶資料時,產生一則通知

設定 Model : app/Models/Customer.php

use App\Traits\HasNotics;   //上方加入

class Customer extends Model {
    use HasFactory, HasNotics;    //加入HasNotics,保留你原有的

    ...

}

修改 CustomerController.php

public function update($id, Request $request) {

        $customer = Customer::find($id);
        $customer->fill($request->all());
        $customer->save();

        // 發送系統內部通知
        $customer->notic()->sendToUser(
            Auth::id(),
            '客戶更新通知',
            '客戶資料已更新'
        );
}

如果用方法1,需要在每次執行的Controller引入NoticService, 但我認為這些通知功能應該是只需要設定一次永久使用,修改的機會不大,所以我認為方法2 直接整合在 Model 會更好,因為在Controller 完全不用做任何引入和設定。

這次亦令我了解到Trait的好處,相信日後會更多機會用到。