Let's say, for example, I want to add a couple of options to php artisan migrate
:
php artisan migrate --core
php artisan migrate --project=0f9ebA2
Is it possible to extend an existing artisan command? I don't want to change any default behavior, just add options. It would be sufficient to have a wrapper class that pre-sets certain options, like changing a config variable or adding a path argument, then passes through everything else to the artisan function as normal.
Stubbed out, php artisan migrate --core
would look something like:
public function handle() {
$this->call('migrate', ['--path' => '/database/migrations/core', ...$this->arguments()]);
}
Stubbed out, php artisan migrate --project=0f9ebA2
would look something like:
public function handle() {
config(['tenant.key' => $this->argument('--project')]);
$this->call('migrate', ['--path' => '/database/migrations/project', ...$this->arguments()]);
}
Laravel's documentation is focused solely on authoring custom commands and occasionally invoking other commands with $this->call()
- it doesn't seem to cover extending existing functionality, or passing arguments through.
Already Tried / Doesn't Work:
php artisan migrate:core
. This will work exactly for the functionality I wish to add, and nothing else - it won't preserve any of migrate
's other options, which are also needed. The goal is to set context for migrate
's core functionality.$this->arguments()
and reattach them to the inner artisan call, but each argument also needs to be in the definition of the custom command - trying to pass an argument that isn't in the commands defined arguments causes the command to be rejected. There doesn't seem to be a way to just wildcard passthrough. Sure I can copy-paste migrate's existing argument definitions, but it won't be flexible with any future core changes or 3rd party packages.migrate
directly. Trying a proof-of-concept alias of the base migrate
:namespace App\Console\Commands;
use Illuminate\Database\Console\Migrations\MigrateCommand;
class MigrateCore extends MigrateCommand {
protected $signature = 'migrate:core';
public function handle() {
parent::handle();
}
}
Results in a hairy BindingResolutionException
from higher up Laravel's core hierarchy:
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\MigrateCore, Illuminate\Database\Migrations\Migrator]
which, to me, feels like embarking on a path not intended.
Try adding this as app/Console/Commands/MigrateCore.php
:
<?php
namespace App\Console\Commands;
use Illuminate\Database\Console\Migrations\MigrateCommand;
class MigrateCore extends MigrateCommand {
public function __construct()
{
$migrator = app("migrator");
$dispatcher = app("events");
$this->signature .= "{--core : Run core migrations}";
parent::__construct($migrator, $dispatcher);
}
public function handle(): void
{
if ($this->option("core")) {
$this->input->setOption("path", "database/migrations/core");
}
parent::handle();
}
}
The error you were getting:
"Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\MigrateCore, Illuminate\Database\Migrations\Migrator]"
is fairly cryptic, but if you look into the comments in Illuminate\Container\Container
where the error is thrown, it starts to make a bit of sense:
// If the type is not instantiable, the developer is attempting to resolve // an abstract type such as an Interface or Abstract Class and there is // no binding registered for the abstractions so we need to bail out.
The constructor for Illuminate\Database\Console\Migrations\MigrateCommand
wants to be injected with an instance of Illuminate\Database\Migrations\Migrator
which in turn is looking for a Illuminate\Database\Migrations\MigrationRepositoryInterface
. But no concrete classes have been bound to that interface yet.
So, instead of just inheriting the constructor from Illuminate\Database\Console\Migrations\MigrateCommand
we initialize those bindings with the app()
helper, and then pass them to the constructor.
Got a bit of help from this Laracasts post.