如果你曾经使用过Laravel框架,你可能听说过服务容器和服务提供者。事实上,它们是 Laravel 框架的支柱,当您启动任何 Laravel 应用程序的实例时,它们会承担所有繁重的工作。
在本文中,我们将大致了解服务容器的全部内容,然后我们将详细讨论服务提供者。在本文的过程中,我还将演示如何在 Laravel 中创建自定义服务提供者。创建服务提供者后,您还需要在 Laravel 应用程序中注册它才能实际使用它,因此我们也将介绍它。
有两个重要的方法boot和register,您的服务提供商可能会实现,在本文的最后一部分中,我们将彻底讨论这两种方法。
在我们深入讨论服务提供者之前,我将尝试介绍服务容器,因为它将在您的服务提供者实现中大量使用。
了解服务容器和服务提供者
什么是服务容器?
用最简单的术语来说,我们可以说 Laravel 中的服务容器是一个盒子,它保存着各种组件的绑定,并且在整个应用程序中根据需要提供它们。
用 Laravel 官方文档的话来说:
Laravel 服务容器是管理类依赖和执行依赖注入的强大工具。
因此,当您需要注入任何内置组件或服务时,您可以在您的构造函数或方法中输入提示,它会自动从服务容器中注入,因为它包含您需要的一切!这不是很酷吗?它使您免于手动实例化组件,从而避免代码中的紧密耦合。
让我们看一个简单的例子来理解它。
Class SomeClass { public function __construct(FooBar $foobarObject) { // use $foobarObject object } }
如您所见,SomeClass需要一个实例FooBar来实例化自身。所以,基本上,它有一个需要注入的依赖项。Laravel 通过查看服务容器并注入适当的依赖项来自动执行此操作。
如果你想知道 Laravel 如何知道服务容器中包含哪些组件或服务,答案就是服务提供者。服务提供者告诉 Laravel 将各种组件绑定到服务容器中。其实就是叫服务容器绑定,需要通过服务提供者来做。
所以注册所有服务容器绑定的是服务提供者,它是通过服务提供者实现的 register 方法完成的。
这应该引发另一个问题:Laravel 如何了解各种服务提供商?也许你认为 Laravel 也应该自动解决这个问题?不幸的是,现在你需要明确地通知 Laravel。
继续查看config /app.php文件的内容。您会找到一个数组条目,其中列出了您的应用可以使用的所有服务提供者。
'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... */ /* * 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文件。
<?php namespace Illuminate\Cache; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider; use symfony\Component\Cache\Adapter\Psr16Adapter; class CacheServiceProvider extends ServiceProvider implements DeferrableProvider { /** * Register the service provider. * * @return void */ 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('cache.psr6', function ($app) { return new Psr16Adapter($app['cache.store']); }); $this->app->singleton('memcached.connector', function () { return new MemcachedConnector; }); } /** * Get the services provided by the provider. * * @return array */ public function provides() { return [ 'cache', 'cache.store', 'cache.psr6', 'memcached.connector', ]; } }
这里要注意的重要一点是register方法,它允许您定义服务容器绑定。如您所见,cache、cache.store、cache.psr6和memcached.connector服务有四个绑定。
基本上,我们通知 Laravel 每当需要解析cache条目时,它应该返回CacheManager. 所以我们只是在服务容器中添加了一种映射,可以通过$this->app.
这是将任何服务添加到 Laravel 服务容器的正确方法。这也让你可以更全面地了解 Laravel 如何通过所有服务提供者的注册方法并填充服务容器!正如我们之前提到的,它从config/app.php文件中获取服务提供者列表。
这就是服务提供商的故事。在下一节中,我们将讨论如何创建自定义服务提供者,以便您可以将自定义服务注册到 Laravel 服务容器中。
1.创建自定义服务提供者
Laravel 已经附带了一个实用的命令行实用工具artisan,它允许您创建模板代码,这样您就不必从头开始创建它。继续并移动到命令行并在应用程序根目录中运行以下命令以创建自定义服务提供程序。
$php artisan make:provider EnvatoCustomServiceProvider Provider created successfully.
这应该在app/Providers目录下创建文件EnvatoCustomServiceProvider.php 。打开文件以查看其中包含的内容。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class EnvatoCustomServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { // } /** * Bootstrap services. * * @return void */ public function boot() { // } }
register正如我们前面所讨论的,当您使用自定义服务提供者时,boot您将在大多数时间处理这两种方法。
该register方法是您定义所有自定义服务容器绑定的地方。另一方面,boot方法是你可以通过 register 方法消费已经注册的服务的地方。在本文的最后一部分中,我们将详细讨论这两种方法,因为我们将通过一些实际用例来了解这两种方法的用法。
2.注册您的定制服务提供商
所以你已经创建了你的自定义服务提供者。那太棒了!接下来,您需要告知 Laravel 您的自定义服务提供者,以便它可以在引导期间与其他服务提供者一起加载它。
要注册您的服务提供者,您只需在config/app.php文件中的服务提供者数组中添加一个条目。将以下行添加到providers数组中:
App\Providers\EnvatoCustomServiceProvider::class,
就是这样——你已经在 Laravel 注册了你的服务提供商!但是我们创建的服务提供者几乎是一个空白模板,暂时没有用。在下一节中,我们将通过几个实际示例来了解您可以使用register和boot方法做什么。
3.创建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,以便我们可以使用它。该类DemoOne尚未创建,但我们稍后会创建。
在 register 方法中,我们使用了bind服务容器的方法来添加我们的服务容器绑定。因此,每当App\Library\Services\DemoOne需要解决依赖关系时,它都会调用闭包函数,并实例化并返回App\Library\Services\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 可以使用反射自动解决它。
Laravel 文档中的一个非常重要的说明:
如果类不依赖于任何接口,则无需将类绑定到容器中。不需要指示容器如何构建这些对象,因为它可以使用反射自动解析这些对象。
另一方面,如果您将接口绑定到某个实现,它会非常有用。在下一节中,我们将通过一个示例来理解它。
如何使用服务提供商的真实示例
让我们假设您要构建一个允许您以各种方式对用户进行身份验证的服务。首先,您想实现两个适配器,JSON和XML,以便一个可以传递相应格式的凭据以针对您的系统进行身份验证。稍后,您可以根据需要插入更多不同格式的适配器。
这是实现 Laravel 服务提供者的完美示例,因为它允许您为您的服务实现多个适配器,然后您可以轻松地在不同的适配器之间切换。
首先,让我们在app/Library/Services/Contracts/AuthenticationServiceInterface.php创建一个非常简单的界面。
<?php // app/Library/Services/Contracts/AuthenticationServiceInterface.php namespace App\Library\Services\Contracts; Interface AuthenticationServiceInterface { public function authenticate($credentials); }
接下来,让我们创建这个接口的两个具体实现。基本上,我们只需要创建两个扩展AuthenticationServiceInterface接口的类。
在app/Library/Services/XmlAuthentication.php中创建XmlAuthentication类。
<?php // app/Library/Services/XmlAuthentication.php namespace App\Library\Services; use App\Library\Services\Contracts\AuthenticationServiceInterface; class XmlAuthentication implements AuthenticationServiceInterface { public function authenticate($xmlData) { // parse the incoming XML data in the $xmlData and authenticate with your db...happens here return 'XML based Authentication'; } }
同样,JsonAuthentication该类进入app/Library/Services/JsonAuthentication.php。
<?php // app/Library/Services/JsonAuthentication.php namespace App\Library\Services; use App\Library\Services\Contracts\AuthenticationServiceInterface; class JsonAuthentication implements AuthenticationServiceInterface { public function authenticate($JsonData) { // parse the incoming JSON data in the $JsonData and authenticate with your db...happens here return 'JSON based Authentication'; } }
现在,我们将绑定一个接口,而不是绑定一个类。重新访问EnvatoCustomServiceProvider.php文件并更改代码,如下所示。
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Library\Services\JsonAuthentication; class EnvatoCustomServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->bind('App\Library\Services\Contracts\AuthenticationServiceInterface', function ($app) { return new JsonAuthentication(); }); } /** * Bootstrap services. * * @return void */ public function boot() { // } }
在这种情况下,我们将App\Library\Services\Contracts\AuthenticationServiceInterface接口绑定到JsonAuthentication实现。因此,每当App\Library\Services\Contracts\AuthenticationServiceInterface需要解决依赖关系时,它都会实例化并返回App\Library\Services\JsonAuthentication对象。现在它更有意义了,不是吗?
让我们快速修改控制器代码。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Library\Services\Contracts\AuthenticationServiceInterface; class AuthenticateController extends Controller { public function index(AuthenticationServiceInterface $authenticationServiceInstance) { // Initialize $credentials by fetching it from the Request object here... echo $authenticationServiceInstance->authenticate($credentials); } }
您可能已经猜到,$authenticationServiceInterface变量应该是App\Library\Services\JsonAuthentication!
在测试上述实现之前,请确保使用以下命令清除缓存。
php artisan cache:clear php artisan config:clear php artisan clear-compiled
现在,当您运行https://your-laravel-url/authenticate/indexURL 时,它应该会打印JSON based Authentication消息。
这种方法的美妙之处在于您可以JsonAuthentication轻松地将实现与另一种交换。假设您想使用XmlAuthentication实现而不是JsonAuthentication. 在这种情况下,您只需在服务提供者EnvatoCustomServiceProvider.php文件中进行以下更改。
找到以下行:
use App\Library\Services\JsonAuthentication;
并将其替换为:
use App\Library\Services\XmlAuthentication;
同样,找到这个:
return JsonAuthentication();
那应该替换为:
return XmlAuthentication();
如果您想用自己的核心实现替换任何核心实现,您可以使用相同的方法。这不仅是bind您可以用于服务容器绑定的方法;Laravel 服务容器提供了多种绑定到服务容器的方式。请查看官方 Laravel文档以获取完整参考。
实现boot方法
下一个候选者是boot方法,您可以使用它来扩展核心 Laravel 功能。在这种方法中,您可以访问使用服务提供者的register方法注册的所有服务。在大多数情况下,您希望在此方法中注册事件***器,当发生某些事情时会触发该事件***器。
让我们看几个需要boot方法实现的示例。
假设您想将自己的自定义表单字段验证器添加到 Laravel。
public function boot() { Validator::extend('my_custom_validator', function ($attribute, $value, $parameters, $validator) { // validation logic goes here... }); }
如果您想注册一个视图作曲家,这是一个完美的地方!实际上,该boot方法经常用于添加视图作曲家!
public function boot() { View::composer( 'demo', 'App\Http\ViewComposers\DemoComposer' ); }
当然,你想先Illuminate\Support\Facades\View在你的服务提供者中导入一个门面。
同样,您也可以在多个视图之间共享数据。
public function boot() { View::share('key', 'value'); }
您还可以使用boot来定义显式模型绑定。
public function boot() { parent::boot(); Route::model('user', App\User::class); }
这些只是演示该boot方法使用的几个示例。你对 Laravel 的了解越多,你就会找到更多实施它的理由!
结论
在本文中,我们从服务容器开始。然后我们查看了服务提供者以及它们是如何与服务容器连接的。
之后,我们开发了一个自定义服务提供者,在文章的后半部分,我们通过了几个实际示例。
- 什么是服务容器?
- 什么是服务提供者?
- 实现boot方法