Junior: питання на співбесіді з теми «Сповіщення»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
2 питання
Mailable - це лист. Notification - це повідомлення, яке може піти листом, у базу, в Slack чи кудись ще, і канал вибирається окремо від змісту.
Створення:
php artisan make:notification VacancyPublished
class VacancyPublished extends Notification
{
public function __construct(public Vacancy $vacancy)
{
}
public function via(object $notifiable): array
{
return ['mail', 'database'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('Вашу вакансію опубліковано')
->line("«{$this->vacancy->title}» тепер видно всім.")
->action('Переглянути', route('vacancies.show', $this->vacancy));
}
/**
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return ['vacancy_id' => $this->vacancy->id];
}
}
Відправлення:
$user->notify(new VacancyPublished($vacancy));
// або багатьом
Notification::send($users, new VacancyPublished($vacancy));
Метод notify() доступний завдяки трейту Notifiable на моделі.
Що дає канал database: сповіщення зберігається в таблиці notifications, і з нього робиться «дзвіночок» в інтерфейсі - $user->unreadNotifications.
Коли достатньо Mailable: одноразовий лист без варіантів доставки - чек, звіт, підтвердження форми. Notification доречний, коли те саме повідомлення має йти різними каналами або користувач сам обирає, як його повідомляти.
Канал 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.
Докладніше в документації: Сповіщення: сповіщення в базі даних