banner comandos personalizados en Laravel

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.

Suscríbete a nuestro boletín

Te enviaremos publicaciones con consejos útiles y múltiples recursos para que sigas aprendiendo.

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.