3

I'm creating a package and want to override the Migrate command but cannot seem to figure out how. It seems like Laravel itself is getting a higher priority over my ServiceProvider, is there a way to gain priority in the override and get my custom class in there via a package?

K1ll3rM
  • 169
  • 8
  • When you say the migrate command, do you mean one in particular? migrate:fresh, migrate:refresh, migrate:status? – IGP Apr 21 '23 at 01:17

2 Answers2

3

I tried to use a ServiceProvider's register() method to bind a custom implementation of Illuminate\Database\Migrations\Migrator but it didn't work.

Although this is not pretty, I suppose you could use composer to autoload your custom implementation of whatever classes you need to override.

For example, let's say you want to override Illuminate\Database\Migrations\Migrator.

Write a new implementation of this class and save somewhere... for example in app/Overrides/Illuminate/Database/Migrations/Migrator.php.

Inside this file, override the vendor class keeping the vendor namespace.

// app/Overrides/Illuminate/Database/Migrations/Migrator.php
namespace Illuminate/Database/Migrations;

class Migrator
{
    ....
}

Then, in your composer.json file, you need to explicitly state you want THIS class to be autoloaded instead of laravel's, and also exclude laravel's own class from the autoloading.

"autoload": {
    ...
    "exclude-from-classmap": ["vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php"],
    "files": ["app/Overrides/Illuminate/Database/Migrations/Migrator.php"]
    ...
},

And finally, run composer dumpautoload. And run it again every time you add a new package with composer require.

IGP
  • 14,160
  • 4
  • 26
  • 43
2

I found out why the replace wasn't working, the original service provider is deferred, meaning it's register and boot methods get called just before the MigrateCommand class is needed. When I deferred my service provider the same way it started to work!

class MigrateServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->scoped(Migrator::class, function(Application $app) {
            return $app->make('migrator');
        });
    }

    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                NewMigrateCommand::class
            ]);
        }
    }

    public function provides(): array
    {
        return [NewMigrateCommand::class, Migrator::class];
    }
}
K1ll3rM
  • 169
  • 8
  • Nice find. I didn't know about this at all. – IGP Apr 21 '23 at 09:08
  • @IGP Do note that this feature seems to be unsupported by Laravel, I don't see any reason for this to break though, unless they make some major changes to how commands work. – K1ll3rM Apr 21 '23 at 09:26