前提
已经成功安装PHP 8.2 和 Laravel 10.x,并且可以正常启动使用。
为什么要用Service
理由很简单,在Laravel中其实各种逻辑的实现都是Service,可以这么说Service才是真正干活的。
如何创建Service
一般来说对Service会有这么几个需求
- 可以用不同的实现去替换同种Service,而不需要大改代码
- 希望能在几乎任何地方可以呼叫到Service,而不需要复杂的代码引用
- 暂时用不到的Service不需要初始化
- 代码里引入一个Service可以自动加载和初始化
常规Service要创建4个Class
- Service Class
- Service Provider Class
- Contract Class
- Facade Class
这儿用一个简单的样例来说明,比如我们需要一个从免费图片网站里拉取图片的服务,假设现在使用 unsplash,这是一个常用的免费图片网站,注册后可以直接申请一个api key,做个demo完全没问题。
那么先建好4个空的Class
- app/Contract/PullImageContract.php
<?php
namespace App\Contracts;
interface PullImageContract
{
/**
* 搜索关键字,返回第一个结果的url
* @return string 图片url
*/
public function pullImageByKeyword(string $keyword): string;
}
- app/Services/PullImageService.php
<?php
namespace App\Services;
use App\Contracts\PullImageContract;
class PullImageService implements PullImageContract{
public static function make(): PullImageContract{
return new PullImageService();
}
public function pullImageByKeyword(string $keyword): string{
return "";
}
}
- app/Providers/PullImageServiceProvider.php
<?php
namespace App\Providers;
use App\Contracts\PullImageContract;
use App\Services\PullImageService;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class PullImageServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register services.
*/
public function register(): void
{
//
$this->app->singleton(PullImageContract::class, static function (): PullImageContract {
return PullImageService::make();
});
$this->app->alias(PullImageContract::class, 'pullimage');
$this->app->alias(PullImageContract::class, PullImageService::class);
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
- app/Facades/PullImage.php
<?php
declare(strict_types=1);
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
*/
final class PullImage extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'pullimage';
}
}
看上去有点繁琐,其实都是有需求才建立的。解释一下为什么要建立这4个class。
- app/Contract/PullImageContract.php: 就是一个interface,意思是规定好一个内部接口,这个服务将会有什么具体实现的功能,每个功能是输入和输出是什么,约定好具体的输入和输出,这样可以借助IDE的功能来检查是否有错误。在这个例子里只有一个功能 pullImageByKeyword ,输入是一个string类型的keyword,返回一个 string类型的 url,可以在注释里多说明详细内容。
- app/Services/PullImageService.php: 就是实现了PullImageContract的具体class
- app/Providers/PullImageServiceProvider.php : 提供了符合PullImageContract规范的服务,目前只是简单地在register里提供了一个单例类,boot暂时不需要初始化什么。
- $this->app->singleton 是设计模式里的单例模式,就是保证只创建一个实体
-
DeferrableProvider 意味着延迟加载,当用到的时候才会加载,可以节省资源
-
app/Facades/PullImage.php: 可以看成一个快捷存取方式,在Laravel大部分使用场景里都可以用 PullImage::pullImageByKeyword 直接调用服务
安装和启动Service
只要简单地设定在 config/app.php,剩下的就让laravel自行完成。
- 自动载入Provider : 在 providers 里加上一行 App\Providers\PullImageServiceProvider::class,
- 别名:在 aliases 里加上一行 'PullImage'=> App\Facades\PullImage::class,
不需要去掉原来的内容,只要加上就行了
'providers' => ServiceProvider::defaultProviders()->merge([
App\Providers\PullImageServiceProvider::class,
])->toArray(),
'aliases' => Facade::defaultAliases()->merge([
'PullImage'=> App\Facades\PullImage::class,
])->toArray(),
使用服务
一般有两种方式使用服务
- 让Laravel 自动注入,在command/controller等地方可以直接写一个参数就可以自动注入,就是这么简单。之所以用 Contract 就是因为PullImageServiceProvider的register里做了绑定,所以laravel知道对应这个Contract 如何找到Service
public function handle(PullImageContract $pullImageContract)
{
$url=$pullImageContract->pullImageByKeyword("...");
}
- 使用Facade快捷调用,基本上laravel内都可以这样使用,在PullImage这个Facade里定义了一个名字 "pullimage",这个名字也是在 PullImageServiceProvider的register里设定了别名,所以也可以顺利找到Service
use App\Facades\PullImage;
$url=PullImage::pullImageByKeyword("....");
更新具体内容
完成以上所有工作后,以后只要更新两个类 PullImageContract 和 PullImageService ,PullImageContract 负责接口定义,PullImageService则是真正的实现代码
总结
- 创建4个Class
- 在 config/app.php 里设定
- 用自动注入和Facade调用服务
- 更新代码主要在 Contract 和 Service