将 Artisan 注入服务 class

Injecting Artisan into Service class

我正在尝试将 Artisan 注入到服务中,这样我就可以避免使用外观。 查看外观 class 参考,我可以看到我应该注入的 class 是:

Illuminate\Console\Application

所以我假设这样做:

<?php

namespace App\Service;

use Illuminate\Console\Application;

class DummyDataService
{
    /**
     * @var Application
     */
    private $application;

    public function __construct(
        Application $application
    ) {
        $this->application = $application;
    }

    public function insertDummyData()
    {
        $this->application->call('db:seed', [
            '--class' => 'DummyDataSeeder'
        ]);
    }
}

...会起作用。但是,我收到以下错误:

BindingResolutionException in Container.php line 824:
Unresolvable dependency resolving [Parameter #2 [ <required> $version ]] in class Illuminate\Console\Application

如果我像这样在 facade 上调用方法,它会起作用:

Artisan::call('db:seed', [
    '--class' => 'DummyDataSeeder'
]);

到目前为止我无法弄清楚问题出在哪里。有没有人遇到过类似的问题?我尽量避免使用立面。

提前致谢。

如果您看一下 Illuminate\Console\Application 的构造函数,您会发现它需要一个 $version 参数并且不提供任何类型的默认值。因此,如果没有明确提供,它将因该依赖性而失败。老实说,这对我来说似乎是一个错误。如果查看其 Symphony 父项 class,您会发现它在其构造函数中提供了一个默认字符串 'UNKNOWN'。如果您将 Illuminate\Console\Application 修改为具有相同的默认值,代码中的命令现在应该可以工作了。

这给您留下了两个选择。

  1. 只需为此实例使用 Artisan facade。对其余外观使用构造函数注入应该没问题,因为这会特别影响 Artisan 外观。
  2. 更改源代码中的构造函数以具有默认值。不太理想,因为每次更新 Laravel 时您的所有更改都将丢失,但这是一个选项。您也许可以制作某种服务提供商,将一个版本也注入所有 Illuminate\Console\Application 个实例,但我不确定。

老实说,我不确定将默认值添加到构造函数中是否会产生无法预料的后果,尽管我认为它们将是最小的,因为它必须在调用它的任何地方显式定义。我什至可以把它变成一个 PR,看看 Taylor 是否评论它或者只是合并它。

你应该注入 Illuminate\Contracts\Console\Kernel 而不是 Illuminate\Console\Application 来实现你想要的,所以你的 class 应该是这样的:

<?php

namespace App\Service;

use Illuminate\Contracts\Console\Kernel;

class DummyDataService
{
    private $kernel;

    public function __construct(Kernel $kernel) 
    {
        $this->kernel = $kernel;
    }

    public function insertDummyData()
    {
        $this->kernel->call('db:seed', [
            '--class' => 'DummyDataSeeder'
        ]);
    }
}