Artisan, la consola interactiva de Laravel es nuestro asistente cuando estamos creando o trabajando sobre un proyecto. En esta publicación vamos a conocer cómo podemos crear comandos personalizados mediante un entretenido ejemplo.
Preparación de nuestro ejemplo
Para que esta publicación sea didáctica, vamos a tener un ejemplo donde un administrador de un sitio web podrá decidir cuándo activar o desactivar el registro de nuevos usuarios.
Los pasos a seguir para la preparación de este tutorial son simples, para comenzar creamos un nuevo proyecto Laravel con Composer ejecutando en la terminal:
composer create-project laravel/laravel comandos
Procedemos ahora con agregar las credenciales necesarias para la conexión a una base de datos en el archivo .env
.
Seguidamente ejecutamos el comando php artisan make:auth
para generar un sitio con registro, login y recuperación de contraseña.
Para manejar la habilitación del registro de nuevos usuarios vamos a crear un modelo llamado Config
con su respectiva migración ejecutando el siguiente comando:
php artisan make:model Config -m
En el modelo app/Config.php
vamos a colocar 2 propiedades, las cuales son:
protected $table = 'config'; protected $guarded = [];
La tabla del modelo no seguirá la convención de nombres en plural pues en ella se almacenará solo la información del sitio web.
La migración generada con este modelo debe quedarnos de esta forma en el método up
:
public function up() { Schema::create('config', function (Blueprint $table) { $table->increments('id'); $table->boolean('allow_registrations')->default(true); $table->timestamps(); }); }
Vamos a dirigirnos al archivo database/seeds/DatabaseSeeder.php
para tener el registro. El archivo lo dejaremos de esta forma:
<?php use App\Config; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { public function run() { Config::create([ 'allow_registrations' => true ]); } }
Finalmente ejecutamos las migraciones junto a los seeders con el siguiente comando:
php artisan migrate --seed
Creación de un comando personalizado
Para crear un nuevo comando de Artisan solo debemos ejecutar la siguiente instrucción en nuestra terminal:
php artisan make:command RegistrationStatus
Este comando creará una nueva clase en el directorio app/Console/Commands
llamada RegistrationStatus
. Puedes además enviar la opción --command
con la cual podemos especificar cómo puede ser llamado el comando desde la terminal.
Lógica de nuestro comando
Vamos a dirigirnos a la clase recién creada en la dirección app/Console/Commands/RegistrationStatus.php
y vamos a comenzar colocando el nombre del comando y su descripción.
protected $signature = 'config:register {activation=on}';
Con la propiedad $signature
se define el nombre o la manera de llamar al comando desde la consola.
Adicionalmente, en nuestro caso también vamos a requerir un parámetro llamado activation
el cual tendrá como valor predeterminado on
.
Los comandos en Laravel pueden tener como entradas de datos: opciones y/o argumentos. Una opción de un comando puede cambiar el comportamiento de un comando y se expresa con el prefijo --
, mientras que un argumento es un valor que puede requerir un comando para trabajar sobre él.
Ahora con la propiedad $description
colocamos la descripción de nuestro comando:
protected $description = 'Enable or disable new registrations';
Por último, vamos a definir la lógica de nuestro comando en el método handle
de esta forma:
use App\Config; public function handle() { $config = Config::allowRegistrations($this->argument('activation') == 'on'); $this->info($config->allow_registrations ? 'El registro de nuevos usuarios está activado' : 'El registro de nuevos usuarios está desactivado' ); }
El método argument
nos ayuda a recuperar un argumento pasado al invocar el comando (de la misma manera existe el método option
si quieres recuperar una opción). Por otro lado, el método info
nos permite enviar un mensaje a la consola, también tenemos disponible el método error
para enviar un mensaje de advertencia o error.
Culminación del ejemplo
Vamos a crear el método estático allowRegistrations
en nuestro modelo Config
creado anteriormente.
public static function allowRegistrations($value) { $config = Config::first(); $config->update(['allow_registrations' => $value]); return $config; }
Con esto ya tenemos nuestro comando funcionando. Al ejecutar php artisan list
podemos verlo incluido en el listado.
En este punto ya nuestro comando funciona, pero para que nuestro ejemplo esté completo debemos restringir el acceso al registro cuando no esté activo. Para ello vamos a crear un middleware el cual se encargará de esto:
php artisan make:middleware AllowRegistrations
Seguidamente, editamos el método handle
para colocar la restricción de acceso:
use App\Config; public function handle($request, Closure $next) { if (Config::first()->allow_registrations) { return $next($request); } return redirect()->to('/')->with('error', 'El registro esta desactivado'); }
Agregamos el middleware recién creado al arreglo $routeMiddleware
del archivo app/Http/Kernel.php
de esta forma:
'allow_registrations' => \App\Http\Middleware\AllowRegistrations::class,
En el archivo de rutas web.php
vamos a realizar algunos cambios para aplicar el middleware sobre las rutas de registro. Es decir, vamos a trabajar con las rutas directamente en el archivo web.php
para configurar la activación o desactivación del registro en la aplicación, dependiendo del estado.
Route::get('/', function () { return view('welcome'); }); Auth::routes(['register' => false]); Route::group(['middleware' => ['web', 'guest', 'allow_registrations']], function () { Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register'); Route::post('register', 'Auth\RegisterController@register'); }); Route::get('/home', 'HomeController@index')->name('home');
Para culminar, vamos a modificar la vista welcome.blade.php
para mostrar un mensaje de advertencia. Agregaremos después de la etiqueta body
el siguiente contenido:
@if (session('error')) <div style="background-color: #000; color: white; text-align: center;"> {{ session('error') }} </div> @endif
Closure Commands
En nuestro ejemplo hemos creado una clase para nuestro comando lo cual puede ser más cómodo. Pero existe la posibilidad de crear comandos con solo un Closure o función anónima desde el archivo routes/console.php
. Laravel por defecto nos muestra un ejemplo de su uso en el mismo archivo:
Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->describe('Display an inspiring quote');
Así podemos crear el comando de nuestro ejemplo de esta forma:
use App\Config; Artisan::command('config:register_clousure {activation=on}', function () { $config = Config::allowRegistrations($this->argument('activation') == 'on'); $this->info($config->allow_registrations ? 'El registro de nuevos usuarios está activado' : 'El registro de nuevos usuarios está desactivado' ); })->describe('Enable or disable new registration');
El resultado seria el mismo pero desde una clase podemos extender a otros métodos en caso de que el comando pueda tener mucha más lógica, el código estaría un poco más ordenado y sería más fácil de encontrar. Así que queda de tu parte elegir la forma de crear comandos que prefieras.
Entrada de datos
Si necesitas crear un comando el cual necesite preguntar, confirmar o seleccionar entre múltiples opciones puedes hacerlo con algunos métodos disponibles.
// Preguntar $name = $this->ask('¿Cual es tu nombre?'); // Preguntar (secreto) $password = $this->secret('¿Cual es tu contraseña?'); // Confirmar if ($this->confirm('¿Deseas continuar?')) { // } // Selección $name = $this->choice('¿Cual es tu nombre?', ['Duilio', 'Clemir'], $porDefecto);
Ejecutar comandos desde la aplicación
Podemos ejecutar comandos con el facade Artisan
desde cualquier lugar de nuestra aplicación esta forma:
Artisan::call('config:register on');
El método call
desde la versión 5.8 puede recibir el primer parámetro con el comando completo. En versiones anteriores las parámetros del comando eran pasados en un arreglo.
Probar comandos de forma automatizada
Además podemos crear una o más pruebas unitarias para comprobar la funcionalidad del comando y otra prueba funcional para comprobar que se pueda acceder o no según el estado del registro.
Comenzamos creando la prueba unitaria con el siguiente comando en nuestra terminal:
php artisan make:test CommandAllowRegistrationsTest --unit
En el test recién creado vamos a colocar 2 pruebas las cuales son las siguientes:
/** @test */ function can_activate_registrations() { $this->artisan('config:register', ['activation' => 'on']) ->expectsOutput('El registro de nuevos usuarios está activado'); $this->assertDatabaseHas('config', [ 'allow_registrations' => true ]); } /** @test */ function can_deactivate_registrations() { $this->artisan('config:register', ['activation' => 'off']) ->expectsOutput('El registro de nuevos usuarios está desactivado'); $this->assertDatabaseHas('config', [ 'allow_registrations' => false ]); }
Con estas pruebas estamos comparando que cuando se ejecute el comando con los argumentos on
y off
se actualice la columna allow_registrations
en nuestra base de datos con el valor correspondiente.
Creamos ahora una Feature Test con el comando:
php artisan make:test AllowRegistrationsTest
Procedemos ahora a colocar 2 pruebas dentro del archivo:
/** @test */ function a_user_cannot_register_if_the_registration_is_deactivated() { $this->artisan('config:register', ['activation' => 'off']) $this->get('/register') ->assertRedirect('/') ->assertSessionHas('error', 'El registro esta desactivado'); } /** @test */ function a_user_can_register_if_the_registration_is_activated() { $this->artisan('config:register', ['activation' => 'on']); $this->get('/register') ->assertSee('Register'); }
Con estas pruebas comprobamos que dependiendo del valor en la columna allow_registrations
se habilitara o no el acceso al registro de nuevos usuarios.
Finalmente, procedemos a ejecutar en la consola las pruebas con vendor/bin/phpunit
(o vendor\bin\phpunit
si usas Windows) y las deberíamos verlas pasar.
Nunca te confíes de pruebas que pasen al primer intento. Regresa al comando RegistrationStatus
e invierte la lógica condicional, re-ejecuta las pruebas y al verlas fallar puedes proceder a arreglar el código; aunque parezca innecesario, de esta manera tienes una mayor garantía de que las pruebas realmente vigilen la calidad de tu código.
Terminamos
Con esto terminamos, durante este ejemplo he tratado de mostrarte un uso sencillo para los comandos en nuestra aplicación Laravel y hemos conocido algunas de las posibilidades que tenemos con este feature.
Puedes encontrar todo el código de la lección en este repositorio. Si te ha gustado este material por favor compártelo en tus redes sociales.
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.