Integrar la ejecución de comandos directamente desde un controlador es una técnica poderosa en Laravel que permite automatizar tareas programadas a través de endpoints HTTP. Esto abre la puerta a disparar procesos como el envío masivo de correos desde una simple petición a tu API, sin necesidad de ejecutar comandos manualmente en la terminal.
¿Cómo ejecutar un comando Artisan desde un controlador?
El primer paso es crear o utilizar un controlador existente. En este caso se trabaja con un Newsletter Controller [0:25]. Dentro de este controlador se declara un método llamado send, cuya responsabilidad es invocar un comando Artisan desde la aplicación.
La clave está en utilizar la facade Artisan y su método call [0:43]. Este método recibe como parámetro el nombre del comando que deseas ejecutar. Por ejemplo, para llamar al comando de envío de verificación de correo electrónico:
php
use Illuminate\Support\Facades\Artisan;
public function send()
{
Artisan::call('send:email-verification --class');
El método Artisan::call() permite ejecutar cualquier comando registrado en tu aplicación, incluyendo los que hayas creado de forma personalizada. Después de la ejecución, se retorna una respuesta en JSON confirmando que el proceso se completó correctamente.
¿Cómo configurar la ruta para el endpoint?
Una vez listo el controlador, es necesario apuntar una ruta hacia ese método. Dentro del archivo api.php se declara una ruta de tipo POST [1:10]:
Esta ruta responde a peticiones POST en la dirección api/newsletter y ejecuta el método send del controlador.
¿Cómo preparar la base de datos para probar el envío?
El comando utilizado verifica en la base de datos usuarios que se hayan creado hace siete días y que además no hayan verificado su correo electrónico [1:18]. Para simular este escenario es necesario:
Ubicar un registro de usuario en la base de datos.
Colocar el campo email_verified_at en nulo.
Ajustar la fecha de creación a siete días antes de la fecha actual.
Con estos datos preparados, el comando encontrará al usuario y le enviará el correo de verificación.
¿Cómo verificar que el correo fue enviado correctamente?
Desde un cliente HTTP como Postman o Insomnia, se envía una petición POST a la ruta api/newsletter [1:42]. Si la respuesta devuelve un código 200, significa que el proceso se ejecutó sin errores.
Para confirmar que el correo llegó, basta con revisar el servidor SMTP configurado, como Mailtrap [1:52]. Ahí se puede visualizar el correo de verificación recibido.
¿Qué más puedes construir con esta técnica?
Esta misma estrategia de llamar comandos desde controladores se puede aplicar a múltiples escenarios dentro de una API. El reto propuesto consiste en crear dos endpoints adicionales [2:03]:
Uno para calificar usuarios.
Otro para calificar productos.
Ambos deben seguir la misma lógica: un controlador que reciba la petición, ejecute la lógica necesaria y devuelva una respuesta apropiada. Esta práctica refuerza la capacidad de reutilizar comandos Artisan como servicios accesibles desde cualquier parte de tu aplicación, no solo desde la línea de comandos.
¿Ya implementaste tus propios comandos personalizados y los llamaste desde un endpoint? Comparte tu experiencia y las variantes que encontraste en los comentarios.
No me parecio el reto de esta clase…
el projecto del curso es una api para puntuar
y no lo esta enseñando bien simplemente dandolo de reto.
Me parece perfecto que no te den todo tan masticado pero no es el caso
en este curso,parece que por terminar el curso en 35 clases hay que ir a 200 por hora…
No hay mucha explicacion detras de lo que esta mostrando..
Solo una mejora, cuando se hace el whereDate podrían escribir <= en lugar de = porque si sólo se usa = entonces solo funcionara para los usuarios que tienen exactamente 7 días de haberse registrado y no para los que tengan más de 7 días.
El reto consistira en pasar dos valores y gestionarlos correctamente, el id del elemento que se va calificar y la calificacion, com dijo alguien famoso, vamo a darle !!
Este curso esta muy interesante pero muy mal explicado,
lo estoy viendo dos o tres veces cada video a 0.85x y leyendo la documentacion pero aun asi me pierdo en muchas partes quiza es solo como una introduccion a estos temas y seria bueno que hagan uno donde expliquen bien cada tema :/ por que este es solo como un repaso
El comando para que se pueda ejecutar de forma programatica necesita una ligera evaluacion si el flag optional schedule esta en el comando o si el promp es true pero ambos no se pueden evaluar en la misma sentencia if:
Clase command modificada usando un flag para ejectuar el comando:
<?php
namespace App\Console\Commands;use App\Notifications\NewsletterNotification;use App\User;use Illuminate\Console\Command;classSendNewsletterCommandextendsCommand{/**
* The name and signature of the console command.
*
* @var string
*/protected $signature ='send:newsletter {emails?* : Array of emails to send the newsletter notification} {--s|schedule : Should execute directly?}';/**
* The console command description.
*
* @var string
*/protected $description ='It sends a newsletter email';/**
* Create a new command instance.
*
* @return void
*/publicfunction__construct(){parent::__construct();}/**
* Execute the console command.
*
* @return int
*/publicfunctionhandle(){ $emails = $this->argument('emails'); $builder =User::query();if($emails){ $builder->whereIn('email', $emails);} $count = $builder->count();if($count){ $execute =true;if(! $this->option('schedule')){if(! $this->confirm('Are you sure to send emails ?')){ $execute =false;}}if($execute){ $this->output->progressStart($count); $builder->whereNotNull('email_verified_at')->each(function(User $user){ $user->notify(newNewsletterNotification()); $this->output->progressAdvance();}); $this->output->progressFinish();return $this->info('Se enviaron '.$count.' emails');}} $this->info('No se envio ningun correo');}}
Podemos llamar ahora al comado de forma programatica por ejemplo en una ruta web:
Hola, les dejo mi solución al reto para calificar productos desde un EndPoint.
.
Primer parte - Prueba
Siguiendo TDD, primero se escribe una prueba, la mía es medio escueta en la respuesta pero fuerte en la comprobación de la DB:
<?php
namespace Tests\Feature\Http\Controllers;use App\Models\Product;use App\Models\User;use Illuminate\Foundation\Testing\RefreshDatabase;use Illuminate\Foundation\Testing\WithFaker;use Tests\TestCase;classRatingControllerTestextendsTestCase{ use RefreshDatabase;/**
* Pruebo un End-Point de mi API que me permita calificar un producto
*
* @return void
*/publicfunctiontest_api_product_rating(){ $this->withoutExceptionHandling(); $user =User::factory()->create(); $product =Product::factory()->create(); $data =['score'=>3.5]; $this->actingAs( $user )->postJson("api/rating/products/$product->id", $data ); $this->assertDatabaseHas('ratings',['score'=> $data['score'],'rateable_type'=>get_class($product),'rateable_id'=> $product->id,'qualifier_type'=>get_class( $user ),'qualifier_id'=> $user->id
]);}}
Básicamente, creo un usuario, luego un producto y luego la información que se enviará en la solicitud API - después actuando como el usuario recien creado publico en el endpoint indicado mi data para luego comprobar que en efecto se creó el registro de la calificación en la tabla ratings. Les dejo el commit en mi repositorio: Pruebo que un usuario pueda calificar un producto desde un EndPoint API
.
Segunda parte - Api route
Para hacer que la prueba pase hay que decirle a Laravel que en efecto existe el endpoint al que queremos pasarle la calificación y lo hacemos de la siguiente forma:
Aquí decimos que cuando se acceda a la ruta especificada se ejecute el método products del RatingController ( aún no creado), les dejo el commit en mi repositorio: End Point protejido por sanctum para calificar producto
.
Tercera parte - Método Products de RatingController
Ahora sí se viene lo bueno (xd), creamos el controlador que indicamos en la ruta (php artisan make:controller RatingController) y definimos lo que el método products deba hacer:
Aquí accedemos al usuario mediante el obejto $request y encadenamos el método rate() para pasarle el $product a calificar y el score del $request y respondemos que todo ok. Les dejo el commit de mi repositorio: método products de RatingController
y ya está! la prueba pasa 🥳
Mi solucion
<?php
namespace App\Console\Commands;use App\Models\User;use App\Notifications\EmailVerificationNotification;use Illuminate\Console\Command;classSendEmailVerificationReminderCommandextendsCommand{/**
* The name and signature of the console command.
*
* @var string
*/protected $signature ='send:reminder {emails?*}';/**
* The console command description.
*
* @var string
*/protected $description ='Send Email verification reminder';/**
* Create a new command instance.
*
* @return void
*/publicfunction__construct(){parent::__construct();}/**
* Execute the console command.
*
* @return int
*/publicfunctionhandle(){ $emails = $this->argument('emails'); $builder =User::query();if($emails){ $builder->whereIn('email', $emails);} $builder->whereNull('email_verified_at'); $count = $builder->count();if($count){ $this->output->progressStart(); $builder->each(function(User $user){ $user->notify(newEmailVerificationNotification()); $this->output->progressAdvance();}); $this->output->progressFinish(); $this->info("$count emails send");}else{ $this->info('No emails without verification');}}}