What is a usage and purpose of Laravel's binding?

后端 未结 1 1164
走了就别回头了
走了就别回头了 2021-01-31 20:32

I cannot understand a point of Laravel\'s binding system. I know what does dependency injection mean. And it can work even without that weird \"bindings\", right? I saw in docum

相关标签:
1条回答
  • 2021-01-31 21:04

    And it can work even without that weird "bindings", right?

    Not really. Sure it can work just fine if the required dependencies are simple to instantiate. Let's say you have this simple class Foo stored in app directory:

    <?php
    
    namespace App;
    
    class Foo
    {
        public function hello()
        {
            return 'Hello World';
        }
    }
    

    You can type hinted this class without binding it first in the container. Laravel will still be able to resolve this class. Let's say you type-hinted it like this in the routes:

    Route::get('/foo', function (App\Foo $foo) {
        return $foo->hello(); // Hello World
    });
    

    We can even go further, let's say this Foo class required another simple class Bar. Our Bar class looks like this:

    <?php
    
    namespace App;
    
    class Bar
    {
        public function hello()
        {
            return 'bar';
        }
    }
    

    And our Foo class looks like this now:

    <?php
    
    namespace App;
    
    class Foo
    {
        public function __construct(Bar $bar)
        {
            $this->bar = $bar;
        }
    
        public function hello()
        {
            return $this->bar->hello();
        }
    }
    

    Will Laravel be able to resolve the type-hinted Foo class now? YES! Laravel will still be able to resolve this Foo class.

    Now the issue will come in when our Foo class needs slightly more complex dependencies that need to be configured. Imagine that our Foo class simply need the name of our application. Sure you can simply use config('app.name') within the class's method, but imagine that this can be an HTTP client that requires a configuration array to instantiate.

    <?php
    
    namespace App;
    
    class Foo
    {
        public function __construct($appName)
        {
            $this->appName = $appName;
        }
    
        public function hello()
        {
            return "Hello {$this->appName}";
        }
    }
    

    Will Laravel be able to solve this class now? NOPE. Service Container to the rescue! You can teach Laravel how to resolve this Foo class by binding it in service container. You can define the binding within the register method on app\Providers\AppServiceProvider.php file:

    public function register()
    {
        $this->app->bind(\App\Foo::class, function ($app) {
            // Pass the application name
            return new \App\Foo($app->config['app.name']);
        });
    }
    

    And sometimes, you don't want multiple instances to be created. Like our Foo class for instance, there's no need for multiple instances for this kind of class. In this case, we can bind it with singleton method.

    $this->app->singleton(\App\Foo::class, function ($app) {
        return new \App\Foo($app->config['app.name']);
    });
    

    More Important Usage

    But the more important usage of this service container is that we can bind an interface to it's implementation. Let's say we have this PaymentProcessorInterface with pay method:

    <?php
    
    namespace App;
    
    interface PaymentProcessorInterface
    {
        public function pay();
    }
    

    Then we have the implementation of this interface named StripeProcessor:

    <?php
    
    namespace App;
    
    class StripeProcessor implements PaymentProcessorInterface
    {
        public function pay()
        {
            return 'pay with stripe';
        }
    }
    

    With service container, we can bind the PaymentProcessorInterface to StripeProcessor class:

    $this->app->bind(\App\PaymentProcessorInterface::class, function () {
        return new \App\StripeProcessor();
    });
    

    We can then type-hinted PaymentProcessorInterface within our code:

    Route::get('/pay', function (App\PaymentProcessorInterface $paymentProcessor) {
        return $paymentProcessor->pay(); // pay with stripe
    });
    

    This way we can easily swap the PaymentProcessorInterface implementation. Let's say we want to change the payment processor to Paypal then we have this PaypalProcessor class.

    <?php
    
    namespace App;
    
    class PaypalProcessor implements PaymentProcessorInterface
    {
        public function pay()
        {
            return 'pay with paypal';
        }
    }
    

    All we have to do is update the binding:

    $this->app->bind(\App\PaymentProcessorInterface::class, function () {
        return new \App\PaypalProcessor();
    });
    

    Hope this gives you some ideas.

    0 讨论(0)
提交回复
热议问题