Middle: питання на співбесіді з теми «Пошта»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
2 питання
Тест не повинен слати справжні листи - це повільно, ненадійно й іноді доходить до реальних людей.
Mail::fake() підміняє транспорт і запамʼятовує, що мало піти:
it('sends a welcome letter on registration', function () {
Mail::fake();
$this->post('/register', [
'email' => 'dev@example.com',
'password' => 'password',
]);
Mail::assertSent(WelcomeMail::class, function (WelcomeMail $mail) {
return $mail->hasTo('dev@example.com');
});
});
Є ще assertNotSent(), assertNothingSent() і assertSentCount().
Важлива пастка: якщо лист відправляється через сповіщення ($user->notify(...)), Mail::fake() його не побачить - потрібен Notification::fake() і assertSentTo(). Плутанина між цими двома - найчастіша причина «тест не бачить листа, хоча він точно йде».
Друга пастка: якщо mailable реалізує ShouldQueue, а в тесті стоїть Queue::fake(), лист не дійде до пошти взагалі - перевіряти треба постановку завдання.
Поза тестами для перегляду верстки зручні два інструменти. Драйвер log пише лист у storage/logs, а Mailpit чи Mailtrap ловлять пошту в локальний ящик - листи виглядають як справжні, але нікуди не йдуть.
Ще одна дрібниця: mailable можна відкрити прямо в браузері, повернувши його з маршруту - зручно для правки шаблону без повторних відправлень.
Markdown-листи - шаблони з готовими компонентами, які Laravel перетворює на адаптивний HTML з вбудованими стилями й одночасно на текстову версію:
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
<x-mail::message>
# Замовлення відправлено
Ваше замовлення №{{ $order->number }} вже в дорозі.
<x-mail::button :url="$trackingUrl">
Відстежити посилку
</x-mail::button>
<x-mail::table>
| Товар | Кількість |
|:------|----------:|
@foreach ($order->items as $item)
| {{ $item->name }} | {{ $item->quantity }} |
@endforeach
</x-mail::table>
Дякуємо,<br>{{ config('app.name') }}
</x-mail::message>
Компоненти й CSS можна опублікувати (vendor:publish --tag=laravel-mail) і змінити під свій бренд. Головна перевага - не потрібно вручну писати табличну верстку, яку вимагають поштові клієнти.
Листи в черзі. Надсилання через SMTP чи API провайдера займає сотні мілісекунд і може впасти. Користувач не має на це чекати:
Mail::to($user)->queue(new OrderShipped($order));
Mail::to($user)->later(now()->addMinutes(10), new OrderShipped($order));
Або завжди в черзі - інтерфейс ShouldQueue на класі, і тоді навіть send() ставить лист у чергу.
SerializesModels у черзі зберігає лише ідентифікатор моделі, а воркер перечитує модель з бази перед надсиланням.
Навіщо afterCommit:
DB::transaction(function () use ($order) {
$order->update(['status' => 'shipped']);
Mail::to($order->customer)->queue(new OrderShipped($order));
// ... ще операції, які можуть впасти
});
Проблеми без нього:
- воркер може взяти завдання до коміту транзакції - модель ще не оновлена чи взагалі не існує, і лист міститиме старі дані або завдання впаде з
ModelNotFoundException; - якщо транзакція відкотилася, лист про «відправлене замовлення» все одно піде.
Mail::to($order->customer)->queue((new OrderShipped($order))->afterCommit());
Тепер лист ставиться в чергу лише після успішного коміту, а при відкаті - не ставиться взагалі. Глобально це вмикає after_commit => true у конфігурації підключення черги.
Обробка збоїв: невдалі листи потрапляють у failed_jobs; можна задати $tries і backoff на класі листа, як у звичайного завдання.
Докладніше в документації: Пошта: листи в черзі й транзакції