这是一篇翻译文章,译文首发于 Laravel 服务提供者指南,转载请注明出处。
若是你使用过 Laravel 框架的话,那么,你不可能没据说过服务容器和服务提供者。事实上,它们是 Lavavel 框架核心,它们完成 Larvel 应用中服务启动的艰巨任务。php
在这篇文章中,咱们将简单介绍「服务容器」,同时还会深刻讲解服务提供者。本教程还将演示如何在 Laravel 中建立一个自定义的服务提供者。另外,若是你须要在 Laravel 中成功使用服务容器,还须要注册它。那么,让咱们开始吧。laravel
实现一个自定义的服务提供者,须要实现两个很是重要的方法:boot 和 register 方法。关于这两个方法将在教程最后一个小节讨论。shell
在学习服务提供者以前,简单介绍一下服务容器,服务容器会在服务提供者中被常用。闭包
简而言之,Laravel 服务容器 是一个用于存储绑定组件的盒子,它还会为应用提供所需的服务。app
Laravel 文档中描述以下:composer
Laravel 服务容器是用于管理类的依赖和执行依赖注入的工具 - Laravel 文档
这样,当咱们须要注入一个内置的组件或服务时,能够在构造函数或方法中使用类型提示功能注入,而后在使用时从服务容器中自动解析出所需实例及其依赖!是否是很酷?这个功能可让咱们从手动管理组件中解脱出来,从而下降系统耦合度。框架
让咱们看一个简单实例来加深理解。ide
<?php Class SomeClass { public function __construct(FooBar $foobarObject) { // use $foobarObject object } }
如你所见,SomeClass 须要使用 FooBar 实例。换句话说它须要依赖其它组件。Laravel 实现自动注入须要从服务容器中查找并执行注入适当的依赖。memcached
若是你但愿了解 Laravel 是如何知道须要将哪一个组件或服务绑定到服务容器中的,答案是经过服务提供者实现的。服务提供者完成将组件绑定到服务容器的工做。在服务提供者内部,这个工做被称之为服务容器绑定,绑定处理由服务提供者完成。函数
服务提供者实现了服务绑定,绑定处理则由 register 方法完成。
同时,这又会引入一个新的问题:Laravel 是如何知道有哪些服务提供者的呢?这个咱们貌似尚未讨论到吧?我到时看到,以前有说 Laravel 会自动的去查找到服务!朋友,你的问题太多了:Laravel 只是一个框架,它不是一个超级英雄,不是么?咱们固然须要去明确的告知 Laravel 框架咱们有哪些服务提供者。
让咱们来瞧瞧 config/app.php 配置文件。你会找到一个用于 Laravel 应用启动过程当中被载入的服务提供者配置列表。
'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ Laravel\Tinker\TinkerServiceProvider::class, /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, ],
以上就是有关服务容器的基本概念。下一节,咱们将焦点汇集到服务提供者这个核心主题上!
若是说服务容器是提供绑定和依赖注入的的工具,那么 服务提供者 则是实现绑定的工具。
让咱们先来看一个内容提供的服务提供者服务来理解它的运行原理。打开 vender/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php 文件。
public function register() { $this->app->singleton('cache', function ($app) { return new CacheManager($app); }); $this->app->singleton('cache.store', function ($app) { return $app['cache']->driver(); }); $this->app->singleton('memcached.connector', function () { return new MemcachedConnector; }); }
这里咱们须要将重点集中在 register 方法中,这个方法用于绑定服务到服务容器。如你所见,这里一共执行了三个服务的绑定处理:cache、cache.store 和 memcached.connector。
而后,当咱们须要在 Laravel 中使用 cache 服务是,服务容器会解析出 CacheManager 实例并返回。也就是说咱们仅仅是提供了一个能够从 $this->app 访问的对应关系表。
经过服务提供者绑定服务是 Laravel 服务容器绑定服务的正确打开方式。同时经过服务提供者的 register 方法,还有利于理解 Laravel 服务容器是如何管理全部的服务的。咱们以前提到过,经过从 config/app.php 配置文件中读取服务提供者配置列表,从将全部服务注册服务容器中。
以上,就是服务提供者和它的故事。下一节,咱们会学习如何建立一个服务提供者来实现将本身的服务注册到 Laravel 服务容器。
Laravel 已经内置了一个用于建立服务提供者的 artisan 命令来简化建立流程。进入命令行模式后执行下面命令来建立服务提供者。
php artisan make:provider EnvatoCustomServiceProvider
运行后会在 app/Providers 目录下建立 EnvatoCustomServiceProvider.php 文件。打开该文件看下它的源码。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class EnvatoCustomServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { // } /** * Register the application services. * * @return void */ public function register() { // } }
以前咱们有提到服务提供者有两个重要方法:boot 和 register 方法,在实现自定义服务提供者时大部分都是在处理这两个方法。
register 方法用于执行服务绑定处理。另外在 boot 方法中可使用全部已绑定的服务。在这个教程的最后一节咱们将学习更多有关这两个方法的细节,但在这里咱们会先了解些这两个方法的使用示例加深理解。
前面咱们建立了一个自定义的服务提供者。接下来须要让 Laravel 知道如何让这个服务提供者同其它服务提供者同样在应用启动时被加载到 Laravel 中。
为了完成注册服务提供者的功能,仅须要将类名加入到 config/app.php 配置文件的 providers 节点。
'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, /* * Package Service Providers... */ Laravel\Tinker\TinkerServiceProvider::class, /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\EnvatoCustomServiceProvider::class, ],
就是如此简单,如今你已经将自定义服务提供者注册到了 Laravel 中。只不过如今这个服务提供者还几乎什么都没有处理。下一节,咱们将以实例演示如何使用 register 和 boot 方法。
起先,咱们来深刻研究 register 方法加深你对这个方法的理解。打开以前建立的 app/Providers/EnvatoCustomServiceProvider.php 文件,加入以下代码。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Library\Services\DemoOne; class EnvatoCustomServiceProvider extends ServiceProvider { public function boot() { } public function register() { $this->app->bind('App\Library\Services\DemoOne', function ($app) { return new DemoOne(); }); } }
这里咱们作了两个处理:
如今建立 app/Library/Services/DemoOne.php 文件。
<?php namespace App\Library\Services; class DemoOne { public function doSomethingUseful() { return 'Output from DemoOne'; } }
而后,在控制器的构造函数中注入依赖。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Library\Services\DemoOne; class TestController extends Controller { public function index(DemoOne $customServiceInstance) { echo $customServiceInstance->doSomethingUseful(); } }
以上即是一个使用绑定的简单方法。事实上,对于这个示例其实并不须要建立一个服务提供者,并实现 register 方法,由于 Laravel 还能够经过 PHP 的方式功能自动解析。
Laravel 文档中对此有一个说明:
若是咱们的依赖无需任何接口,则无需将类绑定到容器。容器此时不须要了解建立对象的具体细节,而能够经过反射功能实现自动注入。
换句话说,若是咱们须要绑定的服务依赖于其它接口,建立服务提供者则颇有必要。接着来看一个实例以加深理解。
首先,建立一个简单的接口 app/Library/Services/Contracts/CustomServiceInterface.php。
<?php // app/Library/Services/Contracts/CustomServiceInterface.php namespace App\Library\Services\Contracts; Interface CustomServiceInterface { public function doSomethingUseful(); }
而后,建立两个基于此接口的具体实现。或者说,建立两个继承此接口的实现类。
一个是定义在 app/Library/Services/DemoOne.php 文件中的 DemoOne 类。
<?php // app/Library/Services/DemoOne.php namespace App\Library\Services; use App\Library\Services\Contracts\CustomServiceInterface; class DemoOne implements CustomServiceInterface { public function doSomethingUseful() { return 'Output from DemoOne'; } }
相似的,还有 app/Library/Services/DemoTwo.php。
<?php // app/Library/Services/DemoTwo.php namespace App\Library\Services; use App\Library\Services\Contracts\CustomServiceInterface; class DemoTwo implements CustomServiceInterface { public function doSomethingUseful() { return 'Output from DemoTwo'; } }
如今,将绑定具体类名修改成绑定接口。打开 EnvatoCustomServiceProvider.php 文件并改为如何代码。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Library\Services\DemoOne; class EnvatoCustomServiceProvider extends ServiceProvider { public function boot() { } public function register() { $this->app->bind('App\Library\Services\Contracts\CustomServiceInterface', function ($app) { return new DemoOne(); }); } }
这里,咱们将 DemoOne 实现类绑定到 AppLibraryServicesContractsCustomServiceInterface 接口。后续,全部依赖 AppLibraryServicesContractsCustomServiceInterface 接口的功能都被解析成 AppLibraryServicesDemoOne 对象。 这个示例是否是更有实际意义呢?
固然,咱们还须要调整下控制器中的代码。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Library\Services\Contracts\CustomServiceInterface; class TestController extends Controller { public function index(CustomServiceInterface $customServiceInstance) { echo $customServiceInstance->doSomethingUseful(); } }
或许你已经猜到 $customServiceInstance 对象是 AppLibraryServicesDemoOne 类的实例!这种方案的优点在于能够很容易的替换掉 DemoOne 这个实现。
假如你想使用 DemoTwo 替换掉 DemoOne 服务。此时,仅需简单的调整下服务提供者中的代码 EnvatoCustomServiceProvider.php。
将:
use App\Library\Services\DemoOne;
替换成:
use App\Library\Services\DemoTwo;
而后替换:
return new DemoOne();
到:
return new DemoTwo();
使用一样的手法甚至能够将自定义的实现替换掉任何核心服务中的依赖。不只如此,除了 bind 方法;Laravel 服务容器还提供多种绑定方法。能够查看 Laravel 服务容器 文档了解更多。
下一个主题是能够扩展 Laravel 核心服务的 boot 方法。在这个方法中,你能够获取全部经过服务提供者注册到容器中的服务。一般,你会在这个方法中注册某些功能完成后须要触发其它操做的事件监听器。
依照惯例看几个示例先。
建立一个用于 Laravel 校验的自定义表单验证器。
public function boot() { Validator::extend('my_custom_validator', function ($attribute, $value, $parameters, $validator) { // validation logic goes here... }); }
也许你想建立一个 view composer。在 boot 方法中建立是个不错的选择。
public function boot() { View::composer( 'demo', 'App\Http\ViewComposers\DemoComposer' ); }
固然在这里须要率先导入 IlluminateSupportFacadesView。
有时,咱们还须要建立一些共享数据。
public function boot() { View::share('key', 'value'); }
甚至能够显示的建立模型绑定。
public function boot() { parent::boot(); Route::model('user', App\User::class); }
这些示例演示了 boot 方法的一些用法。只有更深刻的理解,才能掌握它的使用方法!
与此同时,咱们须要说再见了。我但愿你喜欢本文所讨论的主题。
本文讨论的是服务提供者,这是本文的中心思想,尽管咱们是以服务容器做为开篇,由于它是理解服务提供者的重要组成部分。
随后,咱们建立了一个自定义服务提供者,而且在本文的后半部分中,咱们介绍了几个实际的示例。