Канал database зберігає сповіщення в таблиці notifications - так будують «дзвіночок» з лічильником непрочитаних в інтерфейсі.
Таблиця:
php artisan make:notifications-table
php artisan migrate
Таблиця містить id (UUID), type (клас сповіщення), поліморфний notifiable, data (JSON), read_at і мітки часу.
Сповіщення:
class InvoicePaid extends Notification
{
public function __construct(public Invoice $invoice) {}
public function via(object $notifiable): array
{
return ['mail', 'database'];
}
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
'url' => route('invoices.show', $this->invoice),
];
}
}
Масив з toArray (чи toDatabase) кодується в JSON і зберігається в колонці data. Зберігайте в ньому все потрібне для показу: ідентифікатори, короткий текст, посилання - а не лише ID, щоб для списку сповіщень не довелося підвантажувати десяток моделей.
Читання:
$user->notifications; // усі, найновіші першими
$user->unreadNotifications; // read_at = null
$user->unreadNotifications()->count();
@foreach (auth()->user()->unreadNotifications as $notification)
<a href="{{ $notification->data['url'] }}">
Рахунок на {{ $notification->data['amount'] }} оплачено
</a>
@endforeach
Позначити прочитаними:
$notification->markAsRead();
$user->unreadNotifications->markAsRead(); // колекцію
$user->unreadNotifications()->update(['read_at' => now()]); // одним запитом
Останній варіант кращий для «позначити все»: не завантажує сповіщення в пам'ять.
Що варто врахувати:
- зміна структури
data: старі записи лишаються у старому форматі. Шаблон має переживати відсутні ключі, а методdatabaseTypeдозволяє зберігати стабільний тип замість імені класу (зручно, якщо клас перейменують); - таблиця росте безкінечно - старі прочитані сповіщення варто видаляти за розкладом;
- індекс на (
notifiable_type,notifiable_id,read_at) прискорює підрахунок непрочитаних; - для показу в реальному часі додають канал
broadcast.
Докладніше в документації: Сповіщення: сповіщення в базі даних