2

I am trying to inject Artisan into a service so I can avoid using the facade. Looking at the facade class reference I can see that the class I should be injecting is:

Illuminate\Console\Application

So I would assume that doing this:

<?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'
        ]);
    }
}

...would work. However, I get the following error:

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

It works if I just call the method on the facade like so:

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

I can't figure out what the problem is so far. Has anyone experienced any similar issues? I try to avoid facades where possible.

Thanks in advance.

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
Steven1978
  • 476
  • 1
  • 3
  • 15

2 Answers2

4

You should inject Illuminate\Contracts\Console\Kernel and not Illuminate\Console\Application to achieve what you want, so your class should look like this:

<?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'
        ]);
    }
}
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
  • Awesome, this works so I have marked as the correct answer. Thanks for your help. – Steven1978 Nov 25 '15 at 09:33
  • 1
    Just wanted to follow up and say that not only does this solution work, but it is totally the correct reference. The docs were actually wrong. I corrected them in a PR that has since been merged so now everything should be right for future people that check. – Andy Noelker Nov 28 '15 at 23:17
1

If you take a peek at the constructor for Illuminate\Console\Application you will see that it expects a $version parameter and does not provide any sort of default. Therefore, if one is not explicitly provided it will fail because of that dependency. Honestly, this seems like a bug to me. If you look at its Symphony parent class, you will see that it provides a default string of 'UNKNOWN' in its constructor. If you modify Illuminate\Console\Application to have that same default, your commands in the code should now work.

This leaves you with two options.

  1. Just use the Artisan facade for this instance. You should be fine using constructor injection for the rest of the facades, as this affects specifically the Artisan facade.
  2. Change the constructor in the source code to have the default. Not ideal, as all of your changes will be lost every time you update Laravel, but it is an option. You might be able to cook up some sort of service provider that injects a version into all Illuminate\Console\Application instances as well, but I'm not sure.

I am honestly unsure if there are unforeseen ramifications of adding that default into the constructor, although I would imagine they would be minimal as it must be explicitly defined everywhere it is called. I might even make this into a PR and see if Taylor comments on it or just merges it.

Andy Noelker
  • 10,949
  • 6
  • 35
  • 47
  • Yeah it does seem like a bug to me too. Thanks for the suggestions. I have for now reverted to using the facade but will keep digging. – Steven1978 Nov 23 '15 at 19:59