• Час читання ~8 хв
  • 21.02.2023

У більшості додатків нам потрібно надсилати сповіщення, будь то в додатку, електронною поштою або Slack - зазвичай це сповіщення про транзакції, щоб попередити користувачів про певну дію чи подію у вашому додатку. Давайте зануримося.

Перше, про що потрібно подумати, бажаючи відправити повідомлення користувачеві, - це механізм доставки. Ми хочемо швидко надіслати електронний лист? Або це системне сповіщення, де ми хочемо надіслати дані на канал Slack? Або, можливо, це сповіщення в додатку, тому що комусь сподобалася фотографія вашого кота?

У будь-якому випадку, доставка лежить в основі будь-якого сповіщення, і, звичайно, з Laravel ви можете доставити на кілька каналів з одного сповіщення. Робимо це за допомогою методу via на самому нашому класі Notification, вказуючи масив каналів, по яких ми хочемо відправити наше повідомлення.

З коробки ви можете надсилати Slack, електронну пошту та в додатку без будь-яких додаткових пакетів. Однак у наші дні є багато варіантів, коли справа доходить до каналів сповіщень. На щастя, веб-сайт, підтримуваний спільнотою, показує вам параметри, інструкції з встановлення та способи надсилання сповіщень через багато різних каналів.

Давайте розглянемо базовий приклад надсилання сповіщення. У нас є система, де люди можуть бронювати зустрічі, думати про Calendly або SavvyCal. Будь то API, веб-взаємодія або навіть CLI, ми завжди будемо хотіти одного і того ж результату. Якщо хтось бронює зустріч, ми повинні повідомити цю особу, повідомивши їй, що зустріч була заброньована, із запрошенням до календаря. Ми також хочемо, щоб особа, з якою заброньована зустріч, повідомила, щоб вона могла знати. Ми також можемо оновити щось у сторонній системі, щоб керувати доступністю.

По-перше, давайте створимо сповіщення за допомогою нашої ремісничої консолі:

php artisan make:notification UserMeetingBookedNotification --test

Let's break down the command. We want to make a notification that part is clear and fits most artisan commands when generating code. We then provide a name for the Notification itself. You could use namespaces to further the grouping of the notification using either "\" or "/" to separate namespaces. We also tag on the option --test so that Laravel will generate a test for this specific notification; this will be a Feature test. I use pestPHP for my testing framework, so I usually publish the stubs to customize the generated test output.

Після того, як ми згенерували наш код сповіщення, ми повинні зосередитися на тому, які властивості повинні бути передані конструктору (якщо такі є) і на які канали ми хочемо доставити. Отже, припустимо, що ми хочемо прийняти ім'я та електронну адресу до сповіщення, щоб ми могли попередити користувача, який забронював зустріч із ними.

Давайте почнемо створювати наше сповіщення:

declare(strict_types=1);

namespace App\Notifications;

use Carbon\CarbonInterface;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

final class UserMeetingBookedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly CarbonInterface $datetime,
    ) {}
}

We can now build our notification to deliver. We need to decide on a channel. For this tutorial, I will focus on the options available by default, but Laravel Notification Channels has a lot of information you can use for alternative channels.

Ми будемо використовувати це сповіщення, щоб надіслати користувачеві електронний лист, щоб повідомити його, що він забронював зустріч. Отже, для цього нам потрібно додати via метод і заявити, що ми хочемо використовувати канал.mail

declare(strict_types=1);

namespace App\Notifications;

use Carbon\CarbonInterface;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

final class UserMeetingBookedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly CarbonInterface $datetime,
    ) {}
    public function via(mixed $notifiable): array
    {
        return ['mail'];
    }
}

Once we have added the via method, we need to describe the Mail Message we will be sending. We do this in the toMail method. The general rule here is that each channel requires a to method, where we prefix the capitalized channel with to.

declare(strict_types=1);

namespace App\Notifications;

use Carbon\CarbonInterface;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

final class UserMeetingBookedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly CarbonInterface $datetime,
    ) {}
    public function via(mixed $notifiable): array
    {
        return ['mail'];
    }
    public function toMail(mixed $notifiable): MailMessage
    {
        return (new MailMessage)
            ->subject('New Booking Received.')
            ->greeting('Booking received')
            ->line("{$this->name} has booked a meeting with you.")
            ->line("This has been booked for {$this->datetime->format('l jS \\of F Y h:i:s A')}")
            ->line("You can email them on {$this->email} if you need to organise anything.");
    }
}

That is it; this will send a nice simple email to the user alerting them how they can get in touch and the fact that they received a booking.

Скажімо, тепер нам потрібно додати це до каналу Slack, можливо, з причин відстеження. Ми все одно хочемо надіслати сповіщення електронною поштою, але додамо канал.

Для початку ми повинні обов'язково протестувати те, що ми вже побудували. Як і у всіх аспектах ваших проектів, ви не можете гарантувати, що все буде працювати, якщо ви не протестуєте їх. Давайте спочатку напишемо наш тест щодо надсилання сповіщення електронною поштою. Тоді ми зможемо внести свої зміни.

it('can send an email notification when a booking is made', function () {
    Notification::fake();

    $user = User::factory()->create();

    expect(
        Bookings::query()->count(),
    )->toEqual(0);

    $action = app()->make(
        abstract: CreateNewBookingAction::class,
    );
    $action->handle(
        user: $user->id,
        name: 'Test User',
        email: '[email protected]',
        time: now()->addHours(12),
    );
    expect(
        Bookings::query()->count(),
    )->toEqual(1);

    Notification::assertSentTo(
        [$user],
        UserMeetingBookedNotification::class,
    );
});

We are faking the notification driver in Laravel so that the notification isn't routed to anyone. We then create a user we want to notify and resolve our logic from the container - abstracting this logic makes testing easier. We want to make sure that when we make a booking, it is saved, but then we want to ensure that the notification was sent as expected, just never delivered.

Ми можемо додати ще одну перевірку в нашому тесті тут, щоб переконатися, що наш тест надсилається на правильний канал:

it('sends the notification to the correct channels', function () {
    Notification::fake();

    $user = User::factory()->create();

    $action = app()->make(
        abstract: CreateNewBookingAction::class,
    );
    $action->handle(
        user: $user->id,
        name: 'Test User',
        email: '[email protected]',
        time: now()->addHours(12),
    );
    Notification::assertSentTo(
        [$user],
        UserMeetingBookedNotification::class,
        function (mixed $notification, array $channels): bool {
            return in_array('mail', $channels);
        },
    );
});

A similar test to our initial one, but this time we are adding a third argument to our assert sent to call, which has a callable where we want to ensure that we are sending to our mail channel.

Тепер ми дуже задоволені нашим покриттям тесту. Ми знаємо, що коли дія буде оброблена, буде надіслано сповіщення, і що коли вона буде надіслана, канали включають поштовий канал - це означає, що наша електронна пошта буде доставлена.

Повернемося до додавання наступного каналу: Slack. Я приховав toMail метод із прикладу коду нижче, щоб було легше побачити, що робиться:Одна річ, яку слід пам'ятати, це те,

declare(strict_types=1);

namespace App\Notifications;

use Carbon\CarbonInterface;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;

final class UserMeetingBookedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly CarbonInterface $datetime,
    ) {}
    public function via(mixed $notifiable): array
    {
        return ['mail', 'slack'];
    }
    public function toSlack(mixed $notifiable): SlackMessage
    {
        return (new SlackMessage)
            ->success()
            ->content("{$this->name} just booked a meeting for {$this->datetime->format('l jS \\of F Y h:i:s A')}.");
    }
}

For this to work correctly, however, we need to make sure we install a first-party notification package by the laravel team:

composer require laravel/slack-notification-channel

Now that this is done, we can amend our test to ensure that we are also sending it to the slack channel:

it('sends the notification to the correct channels', function () {
    Notification::fake();

    $user = User::factory()->create();

    $action = app()->make(
        abstract: CreateNewBookingAction::class,
    );
    $action->handle(
        user: $user->id,
        name: 'Test User',
        email: '[email protected]',
        time: now()->addHours(12),
    );
    Notification::assertSentTo(
        [$user],
        UserMeetingBookedNotification::class,
        function (mixed $notification, array $channels): bool {
            return in_array('mail', $channels)
                & in_array('slack', $channels);
        },
    );
});

There are more tests we can do with our Notifications, but this is an excellent place to start without overcomplicating it. We know that we will send the correct Notification at the right time to the right user on the right channels.

що сповіщення не повинні стояти в черзі. Їх також можна надсилати синхронно.

Отже, тепер ми знаємо, як створити та протестувати сповіщення; Давайте розглянемо їх відправку.

У стандартній програмі Laravel ваша модель користувача реалізує цю рису, яка дозволяє вам зробити щось на зразок наступного:

auth()->user()->notify(new UserMeetingBookedNotification(
    name: $request->get('name'),
    email: $request->('email'),
));

This is great, but the notifiable trait relies on the model having an email property that is accessible. What do we do when our use case does not quite fit this approach? The Notifiable trait is then not of much use to us. This is where I find the Notification facade to be your best friend. You can use the Notification facade to manually route a notification instead of leaning on the trait itself. Let's look at an example:

Notification::send(['email address one', 'email address two'], new EmailNotification($arguments));

The above example will allow you to programmatically send a notification to an array of email addresses quickly and easily. A slightly cleaner way to do so would be to use the on-demand notifications. Let's say we want to be able to send a notification as part of an artisan command programmatically. You can provide an argument for the channel and programmatically select how to send the notification.

Вона починається з фасаду Notifiable сповіщень. Потім ви повідомляєте про це route сповіщенню з каналом і аргументом.

Notification::route('slack', 'slack-webhook-url')
    ->notify(new SlackNotification($argument));

The Notification facade is very powerful and flexible, allowing you to send notifications on demand and test them quickly.

Як ви використовуєте сповіщення у своїх додатках Laravel? Який ваш улюблений канал? Повідомте нас у Twitter!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297