Laravel 中的服务提供者:Service Providers 是什么以及如何使用
对于那些没有经常在 Laravel 中使用 Service Providers 的人来说,这是一个神秘的「术语」:他们实际上「提供」了哪些「服务」,它们究竟是如何工作的?我将在这篇文章中解释它。
默认 Laravel 服务提供者
让我们从 Laravel 中包含的默认服务提供者开始,它们都在 app/Providers
文件夹中:
- AppServiceProvider
- AuthServiceProvider
- BroadcastServiceProvider
- EventServiceProvider
- RouteServiceProvider
它们都是 PHP 类,每个都与它的主题相关:通用「应用程序」、Auth、广播、事件和路由。
并且它们都有一个共同点:boot()
方法。
在该方法内部,你可以编写与这些部分之一相关的任何代码:身份验证、事件、路由等。换句话说,服务提供者只是注册一些全局功能的类。
它们被分离为「Providers」,因为它们在应用程序生命周期的早期执行, 所以这很方便来调用一些全局方法,在执行脚本还没到达模型或控制器之前。
RouteServiceProvider 中有很好的例子,我们来看看它的代码:
class RouteServiceProvider extends ServiceProvider
{
public const HOME = '/dashboard';
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
}
这是配置路由文件的类,默认包含routes/web.php
和routes/api.php
。 请注意,API 的配置不同:路由和中间件为 api
用于是它所用的。
你可以随意去编辑这些服务提供者为你想要的,它们并不在 vendor
文件夹。
一个典型的例子,当你有很多路由想将它们拆分成自定义路由文件时:创建一个 routes/auth.php
路由文件,然后在 RouteServiceProvider
的 boot()
方法中「启用」该文件,只需添加第三行代码:
Route::middleware('web') // 或者你想要添加其他中间件?
->group(base_path('routes/auth.php'));
其他的默认提供者的功能请自行探索。除了 AppServiceProvider
之外,它是空的,就像一个占位符,供我们添加与某些全局应用程序设置相关的任何代码。
一个常用的案例是,向 AppServiceProvider
添加 禁用懒加载。 仅需 添加两行代码 在 boot()
方法:
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;
public function boot()
{
Model::preventLazyLoading(! $this->app->isProduction());
}
如果某些关系模型没有预先加载,这将引发异常,这会导致所谓的 N+1 查询性能问题。
什么时候服务提供者被执行?
如果你看过 请求生命周期,这些是最开始执行的操作:
- public/index.php
- bootstrap/app.php
- app/Http/Kernel.php 还有中间件
- Service Providers: 正是我们这篇文章的主题
config/app.php
文件的 providers
数组中决定了那些服务提供者被加载:
return [
// ... other configuration values
'providers' => [
/*
* Laravel 框架 Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
// ... 来自 /vendor 其他框架提供者
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* 公共 服务提供者 - 我们上面提到的
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
];
如你所见, 有来自/vendor
文件夹的非公共服务提供商列表,你不应该触碰或编辑它们。 我们感兴趣的那些是在底部,默认情况下,broadcastServicerProvider
禁用,可能是因为很少使用。
所有这些服务提供商都是从上到下执行的,迭代该列表两次。
第一次迭代是寻找可选方法register()
,该方法可用于在启动boot()
方法之前启动某些内容。我没有使用过它的经验。
然后,第二次迭代执行所有提供程序的boot()
方法。同样,从providers
数组的顶部到底部。
再然后,在处理完所有服务提供商之后,Laravel开始解析路由、执行控制器、使用模型等。
创建你自己的服务提供者
除了现有的默认文件之外,你还可以轻松创建服务提供者,实现一些其他功能,如 auth/event/routes。
一个典型的例子是与 Blade 视图相关的配置。如果你想创建 Blade 指令,你可以将该代码添加到任何服务提供者的 boot()
方法中,包括默认的AppServiceProvider
,但开发人员通常会创建一个单独的 ViewServiceProvider。
你可以使用以下命令生成它:
php artisan make:provider ViewServiceProvider
它将生成默认模板:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
你可以删除 register()
方法,并在 boot()
内添加 Blade 指令代码:
use Illuminate\Support\Facades\Blade;
public function boot()
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
ViewServiceProvider的另一个示例是关于视图组合器,这是[来自官方Laravel文档]的片段](learnku.com/docs/laravel/9.x/views...):
use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function boot()
{
// 使用基于类的组合器......
View::composer('profile', ProfileComposer::class);
// 使用基于关闭的组合器....
View::composer('dashboard', function ($view) {
//
});
}
}
要执行,应将此新提供程序添加到config / app.php
中的提供程序数组中,如上所述:
return [
// ... 其他配置值
'providers' => [
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
// 在此添加你的提供者
App\Providers\ViewServiceProvider::class,
],
];
开源项目的示例
最后,我想提到一些来自免费的Laravel项目的例子
1. spatie/freek.dev: BladeComponentServiceProvider
一家知名公司Spatie已经在Freek Van der Herten的个人博客上发布了源代码, 其中包含这个文件。
app/Providers/BladeComponentServiceProvider.php:
namespace App\Providers;
use App\Http\Components\AdComponent;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class BladeComponentServiceProvider extends ServiceProvider
{
public function boot()
{
Blade::component('ad', AdComponent::class);
Blade::component('front.components.inputField', 'input-field');
Blade::component('front.components.submitButton', 'submit-button');
Blade::component('front.components.textarea', 'textarea');
Blade::component('front.components.textarea', 'textarea');
Blade::component('front.components.shareButton', 'share-button');
Blade::component('front.components.lazy', 'lazy');
Blade::component('front.components.postHeader', 'post-header');
Blade::component('front.layouts.app', 'app-layout');
}
}
2. monicahq/monica: MacroServiceProvider
最受欢迎的 Laravel 开源项目之一,使用有一个单独的文件来注册 Collection 宏:
app/Providers/MacroServiceProvider.php:
namespace App\Providers;
use App\Helpers\CollectionHelper;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
class MacroServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
if (! Collection::hasMacro('sortByCollator')) {
Collection::macro('sortByCollator', function ($callback, $options = \Collator::SORT_STRING, $descending = false) {
/** @var Collection */
$collect = $this;
return CollectionHelper::sortByCollator($collect, $callback, $options, $descending);
});
}
if (! Collection::hasMacro('groupByItemsProperty')) {
Collection::macro('groupByItemsProperty', function ($property) {
/** @var Collection */
$collect = $this;
return CollectionHelper::groupByItemsProperty($collect, $property);
});
}
if (! Collection::hasMacro('mapUuid')) {
Collection::macro('mapUuid', function () {
/** @var Collection */
$collect = $this;
return $collect->map(function ($item) {
return $item->uuid;
})->toArray();
});
}
}
}
3. phpreel/phpreelcms: DashboardComponentsServiceProvider
Laravel CMS称为Phpreel的Laravel CMS还拥有Blade组件的服务提供商,命名更长。
app/Providers/DashboardComponentsServiceProvider.php:
namespace App\Providers;
//[//]: # (Section Separator)
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use App\Helpers\FileUpload\UploadComponents;
class DashboardComponentsServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
Blade::directive('uploadForm', function () {
$component = UploadComponents::getUploadForm();
$html = '<?php echo \'' . $component . '\'; ?>';
return ('<?php echo "' . $component . '"; ?>');
});
}
}
你还可以在我的网站 LaravelExamples.com上找到更多服务提供商的示例。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。