Laravel: dependency injection in commands

2019-06-23 17:45发布

问题:

Is dependency injection of a custom class in a command possible?

I'm trying this:

<?php

namespace vendor\package\Commands;

use Illuminate\Console\Command;
use vendor\package\Models\Log;
use vendor\package\Updates\UpdateStatistics;

class UpdatePublishmentStats extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'vendorname:updatePublishmentStats';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Updates Twitter followers & Facebook page likes';

    /**
     * Contact implementation
     * @var vendor\package\Update\UpdateStatistics
     */
    protected $stats;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(
        Log $log,
        UpdateStatistics $stats
    ) {
        parent::__construct();
        $this->log = $log;
        $this->stats = $stats;
    }

But when I try to do this:

public function handle()
{
    $this->stats->updateFbStats();

}

I suddenly get Segmentation fault: 11

When I delete the use vendor\package\Updates\UpdateStatistics; part, I don't get that error.

So what am I doing wrong here? Is it not possible to use dependency injection in a command?

回答1:

According to the Command Structure section of 5.2 documentation (https://laravel.com/docs/5.2/artisan#writing-commands):

"Note that we are able to inject any dependencies we need into the command's constructor. The Laravel service container will automatically inject all dependencies type-hinted in the constructor."

So I think you're good there, as far as the capability being present and available.

As for getting it to work, for me the segfault points to something wrong with the UpdateStats class, how it's referenced in the service container, or how its being resolved from the service container.

I don't have a definitive answer, but what I would do is try another class and see if I could localize the issue to this particular class, or if the problem happens with others, and then try and debug from there.

Also, if you just can't get that to work, the app() function will resolve items from the service container when you want (although looking through the 5.2 docs I don't see it anymore, so it may be deprecated - I do see $this->app->make() however).

This may work for you if nothing else does:

public function __construct(
    Log $log,
) {
    parent::__construct();
    $this->log = $log;
    $this->stats = app(UpdateStatistics::class);
}

My guess is, however, that you will get a segfault with this as well, as it should try resolving the same class the same way. If you do, then at least the error is a little clearer, and unrelated to auto-injecting feature.

Hope that at least helps a little.


Update on the app() function

So the app() function does not appear to be documented, but I have 5.2 installed right now and the helpers.php file in Illuminate/Foundation definitely has the function:

if (! function_exists('app')) {
    /**
     * Get the available container instance.
     *
     * @param  string  $make
     * @param  array   $parameters
     * @return mixed|\Illuminate\Foundation\Application
     */
    function app($make = null, $parameters = [])
    {
        if (is_null($make)) {
            return Container::getInstance();
        }

        return Container::getInstance()->make($make, $parameters);
    }
}

Unfortunately the API documentation doesn't include any of the helper functions, but the current master, 5.2, and 5.3 versions of the file on Github all have the function:

  • https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/helpers.php#L91
  • https://github.com/laravel/framework/blob/5.3/src/Illuminate/Foundation/helpers.php#L91
  • https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L91


回答2:

You can inject any service in the handle method:

Note that we are able to inject any dependencies we need into the command's handle method.

Source: https://laravel.com/docs/5.8/artisan#command-structure