Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

BloomWebbAB's avatar

Dependency injection: pass parameters to dependency constructor

I'm building a Laravel project and in one of the controllers I'm injecting two dependencies in a method:

public function pusherAuth(Request $request, ChannelAuth $channelAuth) { ... }

My question is really simple: How do I pass parameters to the $channelAuth dependency?

At the moment I'm using some setters to pass the needed dependencies:

public function pusherAuth(Request $request, ChannelAuth $channelAuth)
{
    $channelAuth
        ->setChannel($request->input('channel'))
        ->setUser(Auth::user());

What are the alternatives to this approach?

P.S. The code needs to be testable.

0 likes
13 replies
pmall's avatar

In AppServiceProvider :

public function register(Request $request, Guard $guard)
{
    $this->app->bind('App\ChannelAuth', function () use ($request, $guard){

        $user = $guard->user();
        $chanel = $request->input('channel');

        return new App\ChannelAuth($user, $chanel);

    });
});

When App\ChannelAuth is resolved this closure will be called.

4 likes
BloomWebbAB's avatar

Thanks @pmall , you pointed me in the right direction. I changed the service provider I previously made and now it looks like the following:

<?php namespace App\Providers;

use Security\ChannelAuth;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;

class ChannelAuthServiceProvider extends ServiceProvider {

    /**
     * @var Request
     */
    protected $request;

    /**
     * @var Guard
     */
    protected $guard;

    /**
     * Bootstrap the application services.
     *
     * @param Request $request
     * @param Guard $guard
     * @return void
     */
    public function boot(Request $request, Guard $guard)
    {
        $this->request = $request;
        $this->guard   = $guard;
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('Bloom\Security\ChannelAuthInterface', function()
        {
            return new ChannelAuth($this->request->input('channel_name'), $this->guard->user());
        });
    }
}
pmall's avatar

@laracast-bloom You are doing it wrong here. The boot and register methods are totally unrelated. The boot method is run after register methods of all service provider has finished running. It is not like a constructor. You have to inject the request and guard in the register method.

BloomWebbAB's avatar

@pmall The problem is I cannot add any additional parameter to the register() method otherwise I'll get an incompatibility with the abstract register() function defined in the abstract ServiceProvider class. Can I do that by overriding the parent's constructor?

Anyway even with my approach is working :S

pmall's avatar

Yes your approach should work but it is tricky, as the closure is run at the time ChannelAuth is resolved, it run after the boot method has run so the dependencies are present.

i didn't remember for the register method. I used to do this :

public function register()
{
    $this->app->call([$this, 'registerChannelAuth']);
}

public function registerChannelAuth(Request $request, Guard $guard)
{
    $this->app->bind('App\ChannelAuth', function () use ($request, $guard) {

        $user = $guard->user();
        $chanel = $request->input('channel');

        return new App\ChannelAuth($user, $chanel);

    });
}
BloomWebbAB's avatar

@pmall Thanks for clarifying. Just another question: why do you use

$this->app->call('registerChannelAuth');

instead of

$this->registerChannelAuth();

? Is it to allow dependency injection to work?

pmall's avatar

Because it injects the dependencies needed. The dependencies of the method are resolved through the container.

You see that by just calling the method you have to inject the dependencies by yourself.

My bad again, it is $this->app->call([$this, 'registerChannelAuth']);.

BloomWebbAB's avatar

I'm receiving the following exception:

BindingResolutionException in Container.php line 872:
Unresolvable dependency resolving [Parameter #0 [ <required> $app ]] in class Illuminate\Support\ServiceProvider

This is how my code looks like now:

/**
 * Register the application services.
 *
 * @return void
 */
public function register()
{
    $this->app->call('App\Providers\ChannelAuthServiceProvider@registerProvider');
}

/**
 * @param Request $request
 * @param Guard $guard
 */
public function registerProvider(Request $request, Guard $guard)
{
    $this->app->bind('Bloom\Security\ChannelAuthInterface', function() use ($request, $guard)
    {
        return new ChannelAuth($request->input('channel_name'), $guard->user());
    });
}

It looks like that Laravel cannot resolve either Request or Guard even if the file is correctly namespaced (the IDE is not complaining).

If instead I use:

$this->app->call([$this, 'registerProvider']);

I get another exception:

ReflectionException in Container.php line 776:
Class hash does not exist
MarkRedeman's avatar

Both Request and Guard aren't available yet when the register method is called. Instead you can use the $app->make() function inside the binding closure such that the make() method is executed when the ChannelAuthInterface is resolved:

/**
 * Register the application services.
 *
 * @return void
 */
public function register()
{
    $this->app->bind('Bloom\Security\ChannelAuthInterface', function($app)
    {
        $request = $app->make(Request::class);
        $guard   = $app->make(Guard::class);
        return new ChannelAuth($request->input('channel_name'), $guard->user());
    });
}
3 likes
pmall's avatar

$this->app->call([$this, 'registerProvider']); should work.

clusteCode's avatar

Hello everyone, after reading this interesting solution I ask myself: How can I choose the class to be implemented on the interface based on the user's ID or his role.

example:

<?php 

$this->app->bind('Bloom\Security\ChannelAuthInterface', function($app)
{
       if( Auth::user()->id == 1)
       {
           return new ChannelAuth($request->input('channel_name'), $guard->user());
       }
       else
       {
          return new ChannelAuth_2 ($request->input('channel_name'), $guard->user());
       }
    
});

I can not access these resources: Auth :: user()->id User :: class

Is there a solution?

Thank you !!!

NielsNumbers's avatar

@clusteCode use

$this->app->bind('Bloom\Security\ChannelAuthInterface', function($app, $guard)
{
       if( $guard->user()->id == 1)
       {
           return new ChannelAuth($request->input('channel_name'), $guard->user());
       }
       else
       {
          return new ChannelAuth_2 ($request->input('channel_name'), $guard->user());
       }
    
});

Please or to participate in this conversation.