I often create custom commands to do things like data migrations.

Once scenario that pops up quite often is that I want to check that all database migrations are applied before doing the data migration.

To make this easier from within a console command, I started with creating a trait which can check this.

The basic idea is to run artisan migrate with the --pretend option and checking the output.

namespace App\Traits;

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Str;

trait ChecksPendingMigrations
{
    public function hasPendingMigrations(): bool
    {
        if (App::environment('testing')) {
            return false;
        }

        Artisan::call('migrate', ['--pretend' => true, '--force' => true]);
        return Str::doesntContain(Artisan::output(), 'Nothing to migrate.');
    }
}

Note that when running tests, I'm skipping the check as the migrations are already applied anyway.

Using it inside a custom command is then very easy and can by done by using the trait and calling the hasPendingMigrations function:

namespace App\Console\Commands;

use App\Traits\ChecksPendingMigrations;
use Illuminate\Console\Command;

final class MySampleCommand extends Command
{
    use ChecksPendingMigrations;

    protected $signature = 'my-sample-command';

    protected $description = 'Sample command which checks pending migrations';

    public function handle(): int
    {
        if ($this->hasPendingMigrations()) {
            $this->error('Run the pending database migrations first');
            return self::FAILURE;
        }

        return self::SUCCESS;
    }
}

A different approach can be to simply apply the migrations instead of checking them. If prefer no to do this as I want to be able to check what is happening.