我正在尝试用新的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。
答案 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如何解决依赖关系?
\App::make(Test::class)
或通过类型提示注入依赖项时("更好的方式"来自我的解决方案),Laravel在绑定中查找该依赖项。当然,请记住,对于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');