乘风原创程序

  • Laravel服务容器绑定的几种方法总结
  • 2020/6/14 10:07:12
  • 绑定基础

    几乎所有的服务容器绑定都是在 服务提供者 中完成。

    在目录结构如下图

    注:如果一个类没有基于任何接口那么就没有必要将其绑定到容器。容器并不需要被告知如何构建对象,因为它会使用 PHP 的反射服务自动解析出具体的对象。

    简单的绑定

    在一个服务提供者中,可以通过 $this->app 变量访问容器,然后使用 bind 方法注册一个绑定,该方法需要两个参数,第一个参数是我们想要注册的类名或接口名称,第二个参数是返回类的实例的闭包:

     $this->app->bind('HelpSpot\API', function ($app) {
     return new HelpSpot\API($app->make('HttpClient'));
    });

    注意到我们将容器本身作为解析器的一个参数,然后我们可以使用该容器来解析我们正在构建的对象的子依赖。

    绑定一个单例

    singleton 方法绑定一个只会解析一次的类或接口到容器,然后接下来对容器的调用将会返回同一个对象实例:

    $this->app->singleton('HelpSpot\API', function ($app) {
     return new HelpSpot\API($app->make('HttpClient'));
    });

    绑定原始值

    你可能有一个接收注入类的类,同时需要注入一个原生的数值比如整型,可以结合上下文轻松注入这个类需要的任何值:

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

    绑定接口到实现

    服务容器的一个非常强大的功能是其绑定接口到实现。我们假设有一个 EventPusher 接口及其实现类 RedisEventPusher ,编写完该接口的 RedisEventPusher 实现后,就可以将其注册到服务容器:

    $this->app->bind(
     'App\Contracts\EventPusher', 
     'App\Services\RedisEventPusher'
    );

    这段代码告诉容器当一个类需要 EventPusher 的实现时将会注入 RedisEventPusher,现在我们可以在构造器或者任何其它通过服务容器注入依赖的地方进行 EventPusher 接口的依赖注入:

    use App\Contracts\EventPusher;
    
    /**
     * 创建一个新的类实例
     *
     * @param EventPusher $pusher
     * @return void
     */
    public function __construct(EventPusher $pusher){
     $this->pusher = $pusher;
    }

    上下文绑定

    有时侯我们可能有两个类使用同一个接口,但我们希望在每个类中注入不同实现,例如,两个控制器依赖 Illuminate\Contracts\Filesystem\Filesystem 契约的不同实现。Laravel 为此定义了简单、平滑的接口:

    use Illuminate\Support\Facades\Storage;
    use App\Http\Controllers\VideoController;
    use App\Http\Controllers\PhotoControllers;
    use Illuminate\Contracts\Filesystem\Filesystem;
    
    $this->app->when(PhotoController::class)
     ->needs(Filesystem::class)
     ->give(function () {
     return Storage::disk('local');
     });
    
    $this->app->when(VideoController::class)
     ->needs(Filesystem::class)
     ->give(function () {
     return Storage::disk('s3');
     });

    标签

    少数情况下,我们需要解析特定分类下的所有绑定,例如,你正在构建一个接收多个不同 Report 接口实现的报告聚合器,在注册完 Report 实现之后,可以通过 tag 方法给它们分配一个标签:

    $this->app->bind('SpeedReport', function () {
     //
    });
    
    $this->app->bind('MemoryReport', function () {
     //
    });
    
    $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

    这些服务被打上标签后,可以通过 tagged 方法来轻松解析它们:

    $this->app->bind('ReportAggregator', function ($app) {
     return new ReportAggregator($app->tagged('reports'));
    });

    扩展绑定

    extend 方法允许对解析服务进行修改。例如,当服务被解析后,可以运行额外代码装饰或配置该服务。extend 方法接收一个闭包来返回修改后的服务:

    $this->app->extend(Service::class, function($service) {
     return new DecoratedService($service);
    });

    总结