Correo electrónico desde el comando CLI
Lucky you! You found an early release chapter - it will be fully polished and published shortly!
This Chapter isn't quite ready...
Rest assured, the gnomes are hard at work
completing this video!
Ya hemos hecho el trabajo previo para nuestra función de correo electrónico recordatorio. Ahora, ¡vamos a crear y enviar los correos!
Plantilla de correo electrónico recordatorio
En templates/email
, la nueva plantilla de correo electrónico será muy similar abooking_confirmation.html.twig
. Copia ese archivo y nómbralo booking_reminder.html.twig
. Dentro, no quiero perder demasiado tiempo en esto, así que simplemente cambia el título del acento para que diga "¡Próximamente!":
{% extends 'email/layout.html.twig' %} | |
{% block content %} | |
<row> | |
<columns> | |
<spacer size="40"></spacer> | |
<p class="accent-title">Coming soon!</p> | |
<h1 class="trip-name">{{ trip.name }}</h1> | |
<img | |
class="trip-image float-center" | |
src="{{ email.image('@images/%s.png'|format(trip.slug)) }}" | |
alt="{{ trip.name }}"> | |
</columns> | |
</row> | |
<row> | |
<columns> | |
<p class="accent-title">Departure: {{ booking.date|date('Y-m-d') }}</p> | |
</columns> | |
</row> | |
<row> | |
<columns> | |
<button class="expanded rounded center" href="{{ url('booking_show', {uid: booking.uid}) }}"> | |
Manage Booking | |
</button> | |
<button class="expanded rounded center secondary" href="{{ url('bookings', {uid: customer.uid}) }}"> | |
My Account | |
</button> | |
</columns> | |
</row> | |
{% endblock %} |
¡Envíalo! ¡Juego de palabras espacial accidental!
Comando Enviar Recordatorio
La lógica para enviar los correos electrónicos tiene que ser algo que podamos programar para que se ejecute cada hora o cada día. ¡El trabajo perfecto para un comando CLI! En tu terminal, ejecuta:
symfony make:command
¡Bah!
symfony console make:command
Llámalo: app:send-booking-reminders
.
¡Ve a comprobarlo! src/Command/SendBookingRemindersCommand.php
. Cambia la descripción a "Enviar correos electrónicos de recordatorio de reserva":
// ... lines 1 - 17 | |
( | |
// ... line 19 | |
description: 'Send booking reminder emails', | |
) | |
class SendBookingRemindersCommand extends Command | |
// ... lines 23 - 70 |
En el constructor, autocablea y establece propiedades para BookingRepository
, EntityManagerInterface
y MailerInterface
:
// ... lines 1 - 21 | |
class SendBookingRemindersCommand extends Command | |
{ | |
public function __construct( | |
private BookingRepository $bookingRepo, | |
private EntityManagerInterface $em, | |
private MailerInterface $mailer, | |
) { | |
parent::__construct(); | |
} | |
// ... lines 31 - 68 | |
} |
Este comando no necesita argumentos ni opciones, así que elimina por completo el método configure()
.
Limpia las tripas de execute()
. Empieza añadiendo un bonito:$io->title('Sending booking reminders')
. Luego, coge las reservas que necesitan que se envíen recordatorios, con $bookings = $this->bookingRepo->findBookingsToRemind()
.
Barra de progreso fácil
Para ser los mejores, mostremos una barra de progreso mientras recorremos las reservas. El objeto $io
tiene un truco para esto. Escribe foreach ($io->progressIterate($bookings) as $booking)
. Esto se encarga de toda la aburrida lógica de la barra de progreso Dentro, tenemos que crear un nuevo correo electrónico. En TripController
, copia ese correo electrónico -incluyendo estas cabeceras- y pégalo aquí.
Pero tenemos que ajustarlo un poco: elimina el archivo adjunto. Y para el asunto: sustituye "Confirmación" por "Recordatorio". Arriba, añade algunas variables por comodidad:$customer = $booking->getCustomer()
y $trip = $booking->getTrip()
. Aquí abajo, mantén los mismos metadatos, pero cambia la etiqueta a booking_reminder
. Esto nos ayudará a distinguir mejor estos correos en Mailtrap.
Ah, y por supuesto, cambia la plantilla a booking_reminder.html.twig
.
Siguiendo con el bucle, envía el correo electrónico con $this->mailer->send($email)
y marca la reserva como recordatorio enviado con$booking->setReminderSentAt(new \DateTimeImmutable('now'))
.
¡Perfecto! Fuera del bucle, llama a $this->em->flush()
para guardar los cambios en la base de datos. Por último, celébralo con$io->success(sprintf('Sent %d booking reminders', count($bookings)))
.
// ... lines 1 - 21 | |
class SendBookingRemindersCommand extends Command | |
{ | |
// ... lines 24 - 31 | |
protected function execute(InputInterface $input, OutputInterface $output): int | |
{ | |
$io = new SymfonyStyle($input, $output); | |
$io->title('Sending booking reminders'); | |
$bookings = $this->bookingRepo->findBookingsToRemind(); | |
foreach ($io->progressIterate($bookings) as $booking) { | |
$trip = $booking->getTrip(); | |
$customer = $booking->getCustomer(); | |
$email = (new TemplatedEmail()) | |
->to(new Address($customer->getEmail())) | |
->subject('Booking Reminder for '.$trip->getName()) | |
->htmlTemplate('email/booking_reminder.html.twig') | |
->context([ | |
'customer' => $customer, | |
'trip' => $trip, | |
'booking' => $booking, | |
]) | |
; | |
$email->getHeaders()->add(new TagHeader('booking_reminder')); | |
$email->getHeaders()->add(new MetadataHeader('booking_uid', $booking->getUid())); | |
$email->getHeaders()->add(new MetadataHeader('customer_uid', $customer->getUid())); | |
$this->mailer->send($email); | |
$booking->setReminderSentAt(new \DateTimeImmutable('now')); | |
} | |
$this->em->flush(); | |
$io->success(sprintf('Sent %d booking reminders', count($bookings))); | |
return Command::SUCCESS; | |
} | |
} |
¡Hora de probar! Ve a tu terminal. Para asegurarte de que tenemos una reserva que necesita que se le envíe un recordatorio, recarga los accesorios con:
symfony console doctrine:fixture:load
Ahora, ¡ejecuta nuestro nuevo comando!
symfony console app:send-booking-reminders
Bien, ¡se ha enviado 1 recordatorio! Y el resultado impresionará a nuestros colegas! Antes de comprobar Mailtrap, vuelve a ejecutar el comando:
symfony console app:send-booking-reminders
"Enviados 0 recordatorios de reserva". ¡Perfecto! Nuestra lógica para marcar las reservas como recordatorios enviados ¡funciona!
Ahora comprueba Mailtrap... ¡aquí está! Como era de esperar, se parece mucho a nuestro correo de confirmación, pero aquí dice "Próximamente": está utilizando la nueva plantilla.
X-Tag
y X-Metadata
Cuando se utiliza "Prueba de Mailtrap", las etiquetas y metadatos de Mailer no se convierten en categorías y variables personalizadas de Mailtrap, como ocurre cuando se envían en producción. ¡Pero aún puedes asegurarte de que se envían! Haz clic en esta pestaña "Información técnica" y desplázate un poco hacia abajo. Cuando Mailer no sabe cómo convertir las etiquetas y los metadatos, los añade como estas cabeceras genéricas personalizadas: X-Tag
y X-Metadata
.
Efectivamente, X-Tag
es booking_reminder
. Genial, ¡también es lo que esperamos!
Vale, ¿nueva función? ¡Comprobado! ¿Pruebas para la nueva función? ¡Eso a continuación!