Cómo evitar que se ejecuten comandos destructivos en Laravel
- Publicado el 20 febrero, 2025
- Palabras: 685
Hace poco me topé con una característica interesante de Laravel que no había visto antes: la capacidad de evitar que se ejecuten comandos destructivos en producción.

En la PR #51376 para Laravel en mayo de 2024, Jason McCreary y Joel Clermont agregaron el trait Illuminate\Console\Prohibitable al framework, que luego se incluyó en la versión 11.9 de Laravel.
Agregar este trait a un comando Artisan proporciona un método prohibit que puede usar para determinar si se debe evitar la ejecución del comando.
#Ejemplo de caso de uso
Ya te puedes imaginar el caso de uso perfecto para esto, al ejecutar el comando php artisan migrants:fresh --seed en la terminal, pensando que estás actualizando tu base de datos local, pero no sabes que tu terminal todavía está conectada al servidor de producción.
Ahí es donde entra en juego el trait IlluminateConsoleProhibitable. Puedes usarlo para evitar que se ejecute el comando, incluso si intentas forzarlo.
#Prohibición de los comandos destructivos de bases de datos de Laravel
Laravel se entrega con los siguientes comandos que tienen aplicado el trait IlluminateConsoleProhibitable:
- migrate:fresh - IlluminateDatabaseConsoleMigrationsFreshCommand
- migrate:refresh - IlluminateDatabaseConsoleMigrationsRefreshCommand
- migrate:reset - IlluminateDatabaseConsoleMigrationsResetCommand
- migrate:rollback - IlluminateDatabaseConsoleMigrationsRollbackCommand
- migrate:wipe - IlluminateDatabaseConsoleWipeCommand
Veamos cómo podemos evitar que estos comandos se ejecuten en un entorno de producción, en el fichero app/Providers/AppServiceProvider.php
declare(strict_types=1);
namespace AppProviders;
use IlluminateDatabaseConsoleMigrationsFreshCommand;
use IlluminateDatabaseConsoleMigrationsRefreshCommand;
use IlluminateDatabaseConsoleMigrationsResetCommand;
use IlluminateDatabaseConsoleMigrationsRollbackCommand;
use IlluminateDatabaseConsoleWipeCommand;
use IlluminateSupportServiceProvider;
final class AppServiceProvider extends ServiceProvider
{
// ...
/**
* Bootstrap any application services.
*/
public function boot(): void
{
WipeCommand::prohibit($this->app->isProduction());
FreshCommand::prohibit($this->app->isProduction());
ResetCommand::prohibit($this->app->isProduction());
RefreshCommand::prohibit($this->app->isProduction());
RollbackCommand::prohibit($this->app->isProduction());
}
}
Como podemos ver en el ejemplo de código anterior, en nuestra clase AppProvidersAppServiceProvider, hemos utilizado el método boot para prohibir que los comandos destructivos se ejecuten en un entorno de producción.
El método prohibit acepta un parámetro booleano. Si el parámetro es verdadero, se prohibirá la ejecución del comando. Si el parámetro es falso, se permitirá la ejecución del comando.
Si intentara ejecutar uno de estos comandos fuera de un entorno de producción, los comandos se ejecutarían como se esperaba. Pero si intentara ejecutar uno de estos comandos en un entorno de producción, vería el siguiente mensaje:
WARN This command is prohibited from running in this environment.
Laravel también viene con un útil método auxiliar IlluminateSupportFacadesDB::prohibitDestructiveCommands para que puedas evitar que los cinco comandos se ejecuten en un entorno de producción con una sola línea de código en lugar de tener que prohibir cada uno individualmente:
declare(strict_types=1);
namespace AppProviders;
use IlluminateSupportFacadesDB;
use IlluminateSupportServiceProvider;
final class AppServiceProvider extends ServiceProvider
{
// ...
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::prohibitDestructiveCommands($this->app->isProduction());
}
}
Como podemos ver en el ejemplo de código, hemos utilizado el método auxiliar IlluminateSupportFacadesDB::prohibitDestructiveCommands y le hemos pasado un valor booleano. Esto es equivalente al ejemplo anterior, pero es más conciso. Agregaré esta línea de código a todos mis proyectos de Laravel a partir de ahora.
#Prohibición de tus propios comandos artisan
Es posible que desee utilizar el atributo IlluminateConsoleProhibitable en sus propios comandos Artisan. Por ejemplo, puede tener un comando que elimine todos los datos de un servicio de terceros, como Stripe, por lo que querrá evitar que se ejecute en un entorno de producción. O bien, puede que desee evitar que se ejecute un comando hasta que se habilite una característica en particular.
Veamos cómo podría utilizar este atributo en sus propios comandos.
Imagine que ha creado un comando que realiza llamadas API a Stripe para borrar todos sus datos. Supondremos que no queremos que este comando se ejecute en un entorno de producción y que solo está destinado a fines de desarrollo local. Echemos un vistazo a cómo se vería el comando:
declare(strict_types=1);
namespace AppConsoleCommands;
use IlluminateConsoleCommand;
use IlluminateConsoleProhibitable;
final class ClearStripeData extends Command
{
use Prohibitable;
protected $signature = 'app:clear-stripe-data';
// ...
public function handle(): int
{
if ($this->isProhibited()) {
return self::FAILURE;
}
// Delete Stripe data...
}
}
En el comando anterior, podemos ver que estamos usando el atributo IlluminateConsoleProhibitable en nuestro comando AppConsoleCommandsClearStripeData.
También hemos agregado una verificación al comienzo del método de control para ver si se prohíbe la ejecución del comando. Si es así, devolvemos self::FAILURE, lo que evita que se ejecute el resto del comando. Si no está prohibido, el resto del comando seguirá ejecutándose como se espera.
Aunque hemos agregado el atributo IlluminateConsoleProhibitable a nuestro comando, el comando seguirá ejecutándose como se espera porque en realidad aún no hemos prohibido su ejecución.
Entonces, nos dirigiremos a nuestra clase AppProvidersAppServiceProvider y agregaremos la siguiente línea de código para prohibir que el comando ClearStripeData se ejecute en un entorno de producción:
declare(strict_types=1);
namespace AppProviders;
use AppConsoleCommandsClearStripeData;
use IlluminateSupportServiceProvider;
final class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
ClearStripeData::prohibit();
}
}
Ahora, si intentáramos ejecutar el comando app:clear-stripe-data en un entorno de producción, veríamos el siguiente mensaje:
WARN This command is prohibited from running in this environment.
Bastante bueno, ¿verdad?
Por supuesto, es posible que estés viendo esto y pensando: "¿Por qué no reemplazo la verificación en la parte superior del comando con if ($this->app->isProduction())?" Y estarías en lo cierto; ese es un enfoque totalmente válido y podría ser más adecuado para ti.
Pero creo que el trait IlluminateConsoleProhibitable es una buena manera de encapsular la lógica para prohibir que un comando se ejecute en un solo lugar. Es particularmente bueno si quieres reutilizar la misma lógica en varios comandos.
Fuente: Ash Allen Design
Inicia la conversación
Hazte miembro de Antonio Jenaro para comenzar a comentar.
Regístrate ahora¿Ya estás registrado? Inicia sesión