2
votes

I would use interface in my Jobs and get contextual implementation of it in Jobs classes.

I read all tuts.

I register it:

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app
            ->when(SendinBlueJob::class)
            ->needs(MessageProviderInterface::class)
            ->give(SendinBlueService::class);
    }
}

I dispatch job:

class MessageObserver
{
    public function created(MessageInterface $message)
    {
        SendinBlueJob::dispatch($message);
    }
}

In job class I want to get binded service:

class SendinBlueJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

     public $message;
     public $messageProvider;

    public function __construct(MessageInterface $message, MessageProviderInterface $messageProvider)
    {
        $this->message = $message;
        $this->messageProvider = $messageProvider;
        $this->handle();
     }

    public function handle()
    {
        dd($this->messageProvider);
    }
}

I can't dispatch job because server throws error:

Too few arguments to function App\Jobs\SendinBlueJob::__construct(), 1 passed in /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php on line 16 and exactly 2 expected

I understand it, but I don't understand why Laravel doesn't injecting binded service if interface is called in constructor.

1

1 Answers

4
votes

You can inject dependencies in the handle method.

class SendinBlueJob implements ShouldQueue
{
    use Dispatchable,
        InteractsWithQueue,
        Queueable,
        SerializesModels;

    public $message;

    public function __construct(MessageInterface $message)
    {
        $this->message = $message;
    }

    public function handle(MessageProviderInterface $messageProvider)
    {
        dd(
            $this->message, // MessageInterface
            $messageProvider // SendInBlueService
        );
    }
}

Update

Solution 1

Because you're using contextual binding, and contextual binding does not work when you dependency inject via a method (which is the only way to do it in a job), consider the following approach...

In your service provider:

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->when(MessagesController::class) // Your controller
                  ->needs(MessageProviderInterface::class)
                  ->give(SendinBlueService::class);
    }
}

In your controller:

class MessagesController extends Controller
{
    protected $messageProvider;

    public function __construct(MessageProviderInterface $messageProvider)
    {
        $this->messageProvider = $messageProvider;
    }

    public function store(Request $request)
    {
        // ...

        // Create your message
        $message = Message::create($request->all());

        // Dispatch your job in the controller
        SendInBlueJob::dispatch($message, $this->messageProvider);

        // ...
    }
}

In your job:

class SendinBlueJob implements ShouldQueue
{
    use Dispatchable,
        InteractsWithQueue,
        Queueable,
        SerializesModels;

    protected $message;

    protected $messageProvider;

    public function __construct(MessageInterface $message, MessageProviderInterface $messageProvider)
    {
        $this->message = $message;

        $this->messageProvider = $messageProvider;
    }

    public function handle()
    {
        dd($this->messageProvider);
    }
}

Solution 2

Alternatively, don't use contextual binding. You could do something similar to the following...

Create a new SendinBlueMessageProviderInterface:

interface SendinBlueMessageProviderInterface extends MessageProviderInterface
{
    //
}

Bind the interface to your implementation.

$this->app->bind(
    SendinBlueMessageProviderInterface::class,
    SendinBlueService::class
);

Now you'll be able to inject the message provider via the handle method.

public function handle(SendinBlueMessageProviderInterface $messageProvider)
{
    dd($messageProvider);
}