为什么这个Laravel 5.4服务提供商不会注册?

时间:2017-01-27 20:46:21

标签: laravel laravel-5.3

我正在尝试用新的Laravel 5.4做一个hello world服务提供商。

我创建了以下服务提供商文件:

//File: app/TestProvider/TestServiceProvider.php

namespace App\TestProvider;

use Illuminate\Support\ServiceProvider;

class TestServiceProvider extends ServiceProvider
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {       
        $this->app->bind('Test', function ($app) {
            return new Test();
        });
    }
}

我在同一名称空间下创建了一个简单的类:

//File: app/TestProvider/Test.php

namespace App\TestProvider;

class Test 
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function helloWorld()
    {       
        echo "hello world";
    }
}

问题是,这不是注册。寄存器方法正在执行,就像我在'bind'方法之前放置一个断路器一样,它执行:

public function register()
{       
    dd("BREAKER");
    $this->app->bind('Test', function ($app) {
        return new Test();
    });
}

因此按预期输出“BREAKER”。但是,如果我把断路器放在闭包中,那么由于某种原因,没有任何事情表明“绑定”方法没有被执行?

有什么想法吗?

编辑:

更多信息:我知道Test类已经注册并且在我能做的正确的命名空间中:

dd(new Test());

在注册方法中,它按预期输出资源ID。

3 个答案:

答案 0 :(得分:3)

解释

提供的闭包仅在解析绑定时运行。这就是为什么它是一个闭包,它可以保存在服务容器中并在程序运行时随时解决。

解决方案

要查看已解析的绑定,请创建一个控制器并解析该控制器中的类:

// File: app/Http/Controllers/TestController.php

namespace App\Http\Controllers;

// This isn't the best way, but it works. See the best way below

class TestController extends Controller {
    public function index()
    {
        return \App::make('Test')->helloWorld();
    }
}

当然,不要忘记注册路线:

// File: routes/web.php

Route::get('/', 'TestController@index');

当您点击主页时,绑定将会解决。

然而,正如我所说,这不是最好的方式,所以我在这里准备了一个更好的方法。更改注册绑定的方式:

// File: app/Providers/TestProvider.php

namespace App\TestProvider;

use Illuminate\Support\ServiceProvider;
use App\TestProvider\Test;

// Better way

class TestServiceProvider extends ServiceProvider
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        // Note: we bind the exact complete class name!
        $this->app->bind(Test::class, function ($app) {
            return new Test();
        });
    }
}

在此之后更改控制器,使其看起来像这样:

namespace App\Http\Controllers;

use App\TestProvider\Test;

class TestController extends Controller {
    /**
     * @var Test $test
     */
    private $test;

    // Let Laravel resolve the dependency on constructing the class
    public function __construct(Test $test)
    {
        $this->test = $test;
    }

    public function index()
    {
        return $this->test->helloWorld();
    }
}

你会看到完全相同的事情发生,但它看起来更优雅,避免冲突。

详细信息

Laravel仅提供了服务容器的高级概述,这对于了解它在内部的工作原理并没有帮助。看到这种情况的最好方法是调用堆栈。

当您这样做时,您会发现Laravel在服务容器中的项目中注册每个类。这意味着无论您是否创建服务提供者,该类都将位于容器中。究竟是怎么回事?

运行php artisan optimize时,Laravel会创建包含项目所有类的数组的文件。当您运行应用程序时,在从服务提供程序注册所有内容后,Laravel会从该文件中注册其余类。

这意味着在您的情况下,如果您没有专门注册Test类,它仍然可以解析。基本上,您只需要注册需要解决的特定指令的类。

那么Laravel如何解决依赖关系?

  1. 当您在构造函数中运行\App::make(Test::class)或通过类型提示注入依赖项时("更好的方式"来自我的解决方案),Laravel在绑定中查找该依赖项。
  2. 当找到依赖项时,它会直接解析与之关联的闭包或类的构造函数。
  3. 当它直接解析构造函数时,它会在构造函数参数中查找类型提示,并以递归方式解析所有这些提示,直到没有其他方法可以解决。
  4. 之后返回已解析的类。
  5. 当然,请记住,对于Laravel来分析类的构造函数,首先需要通过服务容器来解决它。你不能只是致电$test = new Test();并期望Laravel能够做所有的魔术:)

    结论

    这是对Laravel服务容器的一个相当快速的概述。当然,学习它的最佳方式是为自己研究资源。它非常优雅,它充分利用了PHP的功能。

    我真的希望这能为您提供服务容器,并在将来帮助您:)

答案 1 :(得分:0)

尝试:

$this->app->bind(Test::class, function ($app) {
    return new Test();
});

答案 2 :(得分:0)

在您实际尝试解析绑定的别名之前,不会执行传递给bind()方法的闭包。

所以,如果你在封闭内部dd('breaker'),那么在Test被解决之前,实际上不会被执行(无论你的首选解决方法是什么):

服务提供商:

// bind the closure to the 'Test' alias
public function register()
{       
    $this->app->bind('Test', function ($app) {
        dd("BREAKER");
        return new Test();
    });
}

解决测试别名的代码:

// different ways of resolving the alias out of the container.
// any of these will execute the bound closure.
$test = resolve('Test');
$test = app('Test');
$test = app()->make('Test');
$test = \App::make('Test');