服务容器

未匹配的标注
本文档最新版为 8.x,旧版本可能放弃维护,推荐阅读最新版!

Laravel 服务容器解析

简介

Laravel 服务容器是管理类依赖和运行依赖注入的有力工具。依赖注入是一个花俏的名词,它实质上是指:类的依赖通过构造器或在某些情况下通过「setter」方法进行「注入」。

来看一个简单的例子:

<?php

namespace App\Http\Controllers;

use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * User Repository 的实现。
     *
     * @var UserRepository
     */
    protected $users;

    /**
     * 创建新的控制器实例。
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * 显示指定用户的详细信息。
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        $user = $this->users->find($id);

        return view('user.profile', ['user' => $user]);
    }
}

在这个例子中,控制器 UserController 需要从数据源中获取 users 。因此,我们要 注入 可以获取 users 的服务。在这种情况下, UserRepository 可能是通过使用 Eloquent 来从数据库中获取 user 信息。因为 UserRepository 是通过注入获取,所以我们可以容易地切换为其他实现。当测试应用程序时,我们还可以轻松地 「mock」 ,或创建假的 UserRepository 实例。

在构建强大的应用程序,和为 Laravel 核心贡献代码时,必须深入理解 Laravel 的服务容器。

绑定

绑定基础

几乎所有服务容器的绑定都是在 服务提供者 中进行的,所以下面的例子将示范在该情景中使用容器。

{tip} 但是,如果类没有依赖任何接口,那么就没有必要将类绑定到容器中了。容器绑定时,并不需要指定如何构建这些类,因为容器中会通过 PHP 的反射自动解析对象。

简单绑定

在服务提供者中,你经常可以通过 $this->app 属性访问容器。我们可以通过 bind 方法注册一个绑定,通过传递注册类或接口的名称、及返回该实例的 Closure 作为参数:

$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'));
});

绑定实例

你也可以使用 instance 方法绑定一个已经存在的对象至容器中。后面的调用都会从容器中返回指定的实例:

$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\Api', $api);

绑定初始数据

有时,你的类不仅需要注入类,还需要注入一些原始数据,如一个整数。此时,你可以容易地通过情景绑定注入需要的任何值:

$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;

/**
 * Create a new class instance.
 *