Correo electrónico desde el comando CLI
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, ¡eso es lo que esperamos también!
Vale, ¿nueva función? ¡Comprobado! ¿Pruebas para la nueva función? ¡Eso a continuación!