Support the ongoing development of Laravel.io →
Article Hero Image

Preventing Destructive Commands from Running in Laravel

17 Feb, 2025 7 min read

Photo by Moritz Mentges on Unsplash

Introduction

I recently stumbled upon a cool Laravel feature I hadn't come across before: the ability to prevent destructive commands from running in production.

So, for those of you like me who haven't heard of this feature before, I thought I'd write a quick article to share it with you. I think it's something you'll really like! I'll be adding to all my Laravel projects from now on.

Preventing Destructive Commands from Running in Laravel

In PR #51376 to Laravel in May 2024, Jason McCreary and Joel Clermont added the Illuminate\Console\Prohibitable trait to the framework, which was then included in the Laravel 11.9 release.

Adding this trait to an Artisan command provides a prohibit method that you can use to determine whether the command should be prevented from running.

Example Use Case

I can already think of a perfect use case for this. When I first started as a junior developer, I typed the command php artisan migrate:fresh --seed in my terminal, thinking I'd be refreshing my local database. Little did I know that my terminal was still connected to the production server. Thankfully, I realised my mistake before hitting ENTER, and I would have only wiped my own website's database, which had no users at the time. But it could have been a disaster - especially if it had been a client's site or app!

P.S. - Don't worry, I've learned my lesson since then! My terminal etiquette has improved significantly.

If I had hit ENTER, Laravel would have asked me to confirm that I wanted to run the command. But it wouldn't have stopped me from running it. So, if I had been in a rush and had not read the confirmation message correctly, I could have still wiped the production database.

That's where the Illuminate\Console\Prohibitable trait comes in. You can use it to prevent the command from running, even if you try to force it.

Prohibiting Laravel's Destructive Database Commands

Laravel ships with the following commands that have the Illuminate\Console\Prohibitable trait applied:

  • migrate:fresh - Illuminate\Database\Console\Migrations\FreshCommand
  • migrate:refresh - Illuminate\Database\Console\Migrations\RefreshCommand
  • migrate:reset - Illuminate\Database\Console\Migrations\ResetCommand
  • migrate:rollback - Illuminate\Database\Console\Migrations\RollbackCommand
  • migrate:wipe - Illuminate\Database\Console\WipeCommand

Let's take a look at how we could prevent these commands from running in a production environment:

declare(strict_types=1);

namespace App\Providers;

use Illuminate\Database\Console\Migrations\FreshCommand;
use Illuminate\Database\Console\Migrations\RefreshCommand;
use Illuminate\Database\Console\Migrations\ResetCommand;
use Illuminate\Database\Console\Migrations\RollbackCommand;
use Illuminate\Database\Console\WipeCommand;
use Illuminate\Support\ServiceProvider;

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());
    }
}

As we can see in the code example above, in our App\Providers\AppServiceProvider class, we've used the boot method to prohibit the destructive commands from running in a production environment.

The prohibit method accepts a boolean parameter. If the parameter is true, the command will be prohibited from running. If the parameter is false, the command will be allowed to run.

If you were to try running one of these commands outside of a production environment, the commands would run as expected. But if you tried running one of these commands in a production environment, you'd see the following message:

WARN  This command is prohibited from running in this environment.

Laravel also ships with a handy Illuminate\Support\Facades\DB::prohibitDestructiveCommands helper method so you can prevent the five commands from running in a production environment with a single line of code rather than needing to prohibit each one individually:

declare(strict_types=1);

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    // ...

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        DB::prohibitDestructiveCommands($this->app->isProduction());
    }
}

As we can see in the code example, we've used the Illuminate\Support\Facades\DB::prohibitDestructiveCommands helper method and passed it a boolean value. This is equivalent to the previous example but is more concise. I'll be adding this line of code to all my Laravel projects from now on.

Prohibiting Your Own Artisan Commands

You may want to use the Illuminate\Console\Prohibitable trait in your own Artisan commands. For example, you might have a command that deletes all data from a third-party service, such as Stripe, so you'll want to prevent that from running in a production environment. Or, you may want to prevent a command from running until a particular feature flag is enabled.

Let's look at how you could use this trait in your own commands.

Imagine you have created a command which makes API calls to Stripe to clear all your data. We'll assume we don't want this command to run in a production environment and is only intended for local development purposes. Let's take a look at what the command might look like:

declare(strict_types=1);

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Console\Prohibitable;

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...
    }
}

In the command above, we can see that we're using the Illuminate\Console\Prohibitable trait in our App\Console\Commands\ClearStripeData command.

We've also added a check at the beginning of the handle method to see if the command is prohibited from running. If it is, we return self::FAILURE, preventing the rest of the command from running. If it's not prohibited, the rest of the command will continue running as expected.

Even though we've added the Illuminate\Console\Prohibitable trait to our command, the command will still run as expected because we've not actually prohibited it from running yet.

So we'll head over to our App\Providers\AppServiceProvider class and add the following line of code to prohibit the ClearStripeData command from running in a production environment:

declare(strict_types=1);

namespace App\Providers;

use App\Console\Commands\ClearStripeData;
use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        ClearStripeData::prohibit();
    }
}

Now, if we were to try running the app:clear-stripe-data command in a production environment, we'd see the following message:

WARN  This command is prohibited from running in this environment.

Pretty cool, right?

Of course, you might be looking at this and thinking, "Why wouldn't I just replace the check at the top of the command with if ($this->app->isProduction())?" And you'd be right; that's a totally valid approach and might be more suitable for you.

But I think the Illuminate\Console\Prohibitable trait is a nice way to encapsulate the logic for prohibiting a command from running in a single place. It's particularly nice if you want to reuse the same logic across multiple commands.

Conclusion

In this article, we've looked at how to prevent destructive commands from running in Laravel applications. We've also seen how we can use the Illuminate\Console\Prohibitable trait to prevent our own commands from running in a production environment.

If you enjoyed reading this post, you might be interested in my 220+ page ebook, "Battle Ready Laravel", which covers similar topics in more depth.

Or, you might want to check out my other 440+ page ebook, "Consuming APIs in Laravel", which teaches you how to use Laravel to consume APIs from other services.

If you'd like to be updated each time I publish a new post, feel free to sign up for my newsletter.

Keep on building awesome stuff! 🚀

Last updated 2 days ago.

driesvints liked this article

1
Like this article? Let the author know and give them a clap!
ash-jc-allen (Ash Allen) I'm a freelance Laravel web developer from Preston, UK. I maintain the Ash Allen Design blog and get to work on loads of cool and exciting projects 🚀

Other articles you might like

Article Hero Image January 14th 2025

Serve a Laravel project on Web, Desktop and Mobile with Tauri

How to display a Laravel project simultaneously on the web, your operating system, and your mobile d...

Read article
Article Hero Image December 13th 2024

How to add WebAuthn Passkeys To Backpack Admin Panel

Want to make your Laravel Backpack admin panel more secure with a unique login experience for your a...

Read article
Article Hero Image December 13th 2024

Quickest way to setup PHP Environment (Laravel Herd + MySql)

Setting up a local development environment can be a time taking hassle—whether it's using Docker or...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2025 Laravel.io - All rights reserved.