简体   繁体   中英

Laravel 5 - How to use $this->app->when()

Reading about laravel bindings ,I understand $this->app->bind , $this->app->singleton and $this->app->instance because they are almost the same.

But $this->app->when is a little bit tricky for me.

In laravel example

$this->app->when('App\Http\Controllers\UserController')
          ->needs('$variableName')
          ->give($value);

In my understanding it injects some primitive value and the App\Http\Controllers\UserController is the alias of the object that will be binded.But where is the object?

Can anyone explain? Thank you for your help.

Contextual binding does not work on variable names, but on types. It is used to provide different implementations for interfaces to consuming classes or functions, depending on the context. In fact, you can actually read the method calls and it does exactly what you'd expect. To elaborate on this, I'll take the example of the documentation and adapt it slightly:

$this->app->when(Worker::class)
          ->needs(Money::class)
          ->give(function () {
              return new Dollar();
          });

$this->app->when(Boss::class)
          ->needs(Money::class)
          ->give(function () {
              return new Cheque();
          });

In this example, Money is an interface and Dollar as well as Cheque are implementations of the interface. The example literally means that if you typehint Money on a Worker class, it will resolve to an instance of Dollar while it will resolve to Cheque on a Boss class. To illustrate, here the implementations and the results:

interface Money
{
    public function getAmount();
}

class Dollar implements Money
{
    public function getAmount()
    {
        return 1;
    }
}

class Cheque implements Money
{
    public function getAmount()
    {
        return 100000;
    }
}

And now we typehint the Money interface to see what we'll get:

class Worker
{
    public function __construct(Money $money)
    {
        echo $money->getAmount(); // prints '1'
    }
}

class Boss
{
    public function __construct(Money $money)
    {
        echo $money->getAmount(); // prints '100000'
    }
}

It means that if a class of UserController is instantiated and it needs a variable with the name $variableName Larvel will automatically resolve this variable with the given value and you don't have to provide it.

For example:

$value = "Sven"
$this->app->when('App\Http\Controllers\UserController')
          ->needs('$userName')
          ->give($value);

This would insert the value 'Sven' into the UserController whenever it needs the variable with the name $userName

In other words, if you had a function like public function __construct(Request $request) Laravel knows what to insert because it knows that a Request object is expected. When you use a function like public function __construct($name) Laravel has no clue what to insert here, essentially you tell Laravel how to resolve this variable with the bindings.

This is an example of primitive binding , for Contextual binding see the answer of @Namoshek

In case anyone finds this thread, trying to achieve contextual binding on an app()->make() or similar situation:

use Closure;
use Illuminate\Container\Container;

class FooBar {

    public function doSomething(): void
    {
       //...do your thing
       $this->getFooObject();
    }

    private function getFooObject(): FooAbstract 
    {
        $class = FooAbstract::class;
        $buildStack = static::class;

        app()->beforeResolving(
                $class,
                Closure::bind(
                    function () use ($buildStack) {
                        $this->buildStack[] = $buildStack;
                    },
                    app(),
                    Container::class,
                )
           );

        return app($class);
    }
}

In the above example laravel's app container is bind as $this to this closure. Since the buildStack property, that's used to identify the object that "needs" the object that is being created, is protected, we have access to it, and we can add the classname to the stack.

for example: if $buildStack = Bar::class ; you can do the following

app()->when(FooBar::class)->needs(FooAbstract::class)->give(FooImplementation::class);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM