En este post vamos a ver cómo se realiza el envío de emails con la nueva funcionalidad de Laravel 5.3: los Mailables.
En este caso, vamos a enviar un email al usuario que recién se acaba de registrar en nuestra aplicación para que active su cuenta. Para ello, haremos lo siguiente:
- Crear una cuenta en Mailtrap.io o usar GMail en su lugar.
- Guardar las credenciales en el archivo de variables de entorno de nuestra aplicación (.env).
- Crear una clase para envio de emails usando mailables.
- Crear una vista del email.
- Crear la tabla para registrar las tareas y crear un job.
- Despachar un Job (llamada al Job) que encole el email desde la funciones que lo requieran.
- Procesar las tareas registradas en la tabla «jobs» de varias formas.
- Crear una ruta necesaria para activar la cuenta de usuario.
- Añadir una función al controlador TokenAuthController para cuando el usuario clique sobre el enlace contenido en el email que enviamos activemos la cuenta de usuario.
Primero registramos una cuenta en Mailtrap.io y luego en el Inbox, copiamos las credenciales y las ponemos en el archivo de variables de entorno Laravel, tal que:
1 2 3 4 5 6 7 8 |
MAIL_ADDRESS=noreply@keepme.es MAIL_DRIVER=smtp MAIL_ENCRYPTION=null MAIL_HOST=mailtrap.io MAIL_NAME="KeepMe" MAIL_PASSWORD=0123456789 MAIL_PORT=2525 MAIL_USERNAME=qwertyuiop |
Para el caso de GMail, estas son las variables a modo de ejemplo que hay que usar (siendo username, password,address y name, las que debes reemplazar por las tuyas):
1 2 3 4 5 6 7 8 |
MAIL_ADDRESS=noreply@keepme.es MAIL_DRIVER=smtp MAIL_ENCRYPTION=ssl MAIL_HOST=smtp.gmail.com MAIL_NAME="KeepMe" MAIL_PASSWORD=0123456789 MAIL_PORT=465 MAIL_USERNAME=tu_cuenta_de_gmail@gmail.com |
Editamos el archivo de configuración de emails, tal que:
1 |
'from' => ['address' => env('MAIL_ADDRESS', null) , 'name' => env('MAIL_NAME', null) ], |
Editamos la migración de la tabla «users» para añadir el campo token de registro:
1 2 3 4 5 6 7 8 9 10 |
class CreateUsersTable extends Migration { public function up() { Schema::create('users', function (Blueprint $table) { . . . . $table->string('registration_token')->nullable(); }); } } |
Editamos el modelo «User» para ocultar el campo creado y no se envíe en las peticiones:
1 2 3 4 5 6 7 8 |
class User extends Authenticatable implements JWTSubject { . . . . protected $hidden = [ .... 'registration_token' ]; } |
Turno del mailable:
1 |
php artisan make:mail AccountActivationEmail |
Abrimos y editamos la clase generada:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
. . . . use App\User; class AccountActivationEmail extends Mailable { use Queueable, SerializesModels; // las propiedades publicas serán // visibles desde la vista del email public $user; public $url; public function __construct(User $user) { $this->user = $user; $this->url = route('account_activation',$user->registration_token); } public function build() { return $this->view('emails.activation') ->to($this->user->email,$this->user->name) ->subject('Activa tu cuenta!'); } } |
Creamos vista de prueba del email:
1 2 3 4 5 |
<p> Hola {{ $user->name }} , por favor confirma tu correo clicando en el siguiente enlace <a href="{{ $url }}">Activa mi cuenta</a> </p> |
Vamos con las colas de Trabajo:
1 2 3 4 5 |
// Crea la migracion de la tabla Jobs php artisan queue:table // Crea la tabla Jobs php artisan migrate |
Editamos la configuración para guardar colas de trabajo en el archivo de variables añadiendo:
1 2 |
. . . . QUEUE_DRIVER=database |
Creamos el job:
1 |
php artisan make:job SendAccountActivationEmail |
Editamos la clase generada:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
use App\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailer; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SendAccountActivationEmail implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels; public $user; public function __construct(User $user) { $this->user = $user; } public function handle(Mailer $mailer) { \Log::info("ENVIANDO EMAIL A {$this->user->email}.....ID: {$this->user->id}." ); $mailer->send(new \App\Mail\AccountActivationEmail($this->user)); } } |
Por último, la llamada al job desde el controlador «TokenAuthController» y su función «signup» que dejamos comentado en este post:
1 2 3 4 5 6 7 8 9 10 11 12 |
public function signup(CreateUserRequest $request) { . . . . $token = JWTAuth::fromUser($user); // Enviamos el mailable a la cola de trabajo $this->dispatch(new \App\Jobs\SendAccountActivationEmail($user)); return response()->success(compact('token')); } } |
Nota: los jobs enviados a la cola son guardados como registros en la base de datos en la tabla «Jobs» y no se envian a «Mailtrap» en este caso.
Para procesar la cola, es decir, todos los emails de los usuarios que se guardaron en la tabla enviarlos a Mailtrap, tenemos varias opciones:
- Desde el Terminal con un comando de Laravel.
- Desde el Terminal con un comando personalizado.
- Mediante una tarea programada (cron task) que ejecute la agenda (schedule) de Artisan desde el Terminal.
- Mediante una tarea programada en tu servidor web (cuando esté la aplicación subida).
1 2 3 |
# Procesamos la cola de trabajo, cada uno de los # registros de la tabla "jobs" php artisan queue:work |
Procesar la cola con un comando personalizado:
1 |
php artisan make:command SendEmails |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class SendEmails extends Command { protected $signature = 'send:emails'; protected $description = 'Send queued emails '; . . . . public function handle() { \Artisan::call('queue:work'); } } |
1 2 3 4 |
protected $commands = [ . . . . Commands\SendEmails::class, ]; |
1 |
php artisan send:emails |
Procesar la cola de tareas programadas de Artisan (Artisan scheduled tasks/commands):
1 2 3 4 5 |
protected function schedule(Schedule $schedule) { // procesamos la cola cada 1 minuto $schedule->command('queue:work')->everyMinute(); } |
1 |
php artisan schedule:run |
Este último comando ejecutado desde el Terminal se quedará esperando y cada 1 minuto ejecutará la cola de tareas registradas en la base de datos en la tabla «jobs» de forma indefinida o hasta que cancelemos pulsando CTRL+C.
Por último, ¿cómo hacemos para que ese comando anterior se ejecute diariamente sin tener que abrir el Terminal y escribir el comando? Pues si estamos en desarrollo (development), podemos crear una tarea programada en Windows para que lo haga o si estamos en producción (production) podemos crear una tarea programada (cron job) en el servidor/hosting donde tengamos nuesta aplicación. El problema de la segunda opción es que es un servicio añadido y por tanto, hay que pagarlo.
Veamos como hacer una tarea programada en Windows(10). Abrir el Programador de tareas y hacer una que se repita diariamente, cada 1 minuto y cada 1 dia. Con la Accion siguiente:
Le ponemos un nombre a la tarea, por ejemplo «Artisan».
El desencadenador es 1 dia cada 1 minuto:
Y por último, la Acción. Ponemos lo siguiente en agregar argumentos: «C:\xampp\htdocs\keepme\artisan» schedule:run
Cuando el usuario pulse sobre el enlace dentro del email para activar su cuenta, Laravel recibirá la petición en la siguiente ruta «/account/activation/token_generado_por_el_job»:
1 2 3 4 5 |
// Verify Account by email Route::get('account/activation/{token}', [ 'uses' => 'TokenAuthController@getConfirmation', 'as' => 'confirmation', ]); |
Y gestionará la activación de la cuenta del usuario poniendo el campo «active» a 1 en la tabla «users»:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public function getConfirmation($token) { try { $user = User::where('registration_token', $token)->firstOrFail(); $user->registration_token = null; $user->active = true; $user->save(); return response()->success('email_confirmed', 200); } catch (Exception $e) { return response()->error('error_verifying_your_email', $e->getStatusCode()); } } |