开源软件名称: jiannei/laravel-api-starter开源软件地址: https://github.com/jiannei/laravel-api-starter开源编程语言:
PHP
82.6%
开源软件介绍: Laravel Api Starter Designed With ❤️
少许的依赖安装,遵循 Laravel 的思维进行扩展,不额外增加「负担」。
开箱即用,加速 Api 开发。
中文文档
社区讨论传送
Lumen/Laravel 学习交流群:1105120693(QQ)
概览
目录结构一览
├── app
│ ├── Console
│ │ ├── Commands
│ │ └── Kernel.php // Schedule 调度
│ ├── Contracts // 定义 interface
│ │ └── Repositories
│ ├── Events
│ │ ├── Event.php
│ │ └── ExampleEvent.php
│ ├── Exceptions // 异常处理
│ │ └── Handler.php
│ ├── Http
│ │ ├── Controllers // Controller 任务分发给不同 Service 处理,返回响应给客户端
│ │ ├── Middleware
│ │ └── Resources // Api Resource 数据转换
│ ├── Jobs // 异步任务
│ │ ├── ExampleJob.php
│ │ └── Job.php
│ ├── Listeners
│ │ └── ExampleListener.php
│ ├── Policies // 权限校验
│ │ └── PostPolicy.php
│ ├── Providers
│ │ ├── AppServiceProvider.php
│ │ ├── AuthServiceProvider.php
│ │ ├── EloquentUserProvider.php // 定制的 EloquentUserProvider,缓存授权用户信息
│ │ ├── EventServiceProvider.php
│ │ └── RepositoryServiceProvider.php // repository 模式架构中,将 interface 与 repository 进行对象绑定
│ ├── Repositories // Repository 层:数据仓库层
│ │ ├── Criteria // 数据查询条件的组装拼接;(可以将公共的或者复杂的查询条件放在这个地方)
│ │ ├── Eloquent // 定义针对某个数据表(或存在关联关系的数据表)的数据维护逻辑;不处理业务(动态数据;实质的 Repository;基于 Eloquent\Model 的封装 )
│ │ ├── Enums // 枚举集合(静态数据)
│ │ ├── Models // Laravel 原始的 Eloquent\Model:定义数据表特性、数据表之间的关联关系等;不处理业务
│ │ ├── Presenters // 配合 Transformer 使用
│ │ ├── Transformers // 响应前的数据转换,作用与 Api Resource 类似,但是功能更丰富
│ │ └── Validators // Eloquent 数据维护前的校验,与表单验证功能类似
│ ├── Services // Service 层:处理实际业务;调用 Repository
│ │ ├── PostService.php
│ │ └── UserService.php
│ └── Support // 对框架的扩展,或者实际项目中需要封装一些与业务无关的通用功能集
│ ├── Serializers // league/fratcal 的 ArraySerializer 扩展,支持简单分页数据格式转换
│ ├── Traits // Class 中常用的辅助功能集
│ └── helpers.php // 全局会用到的辅助函数
Repository & Service 模式架构
在添加这部分描述的时候,联想到了 Vue 中的 Vuex,熟悉 Vuex 的同学可以类比一下。
Controller => dispatch,校验请求后分发业务处理
Service => action,具体的业务实现
Repository => state、mutation、getter,具体的数据维护
实际案例
为了更好地理解 Repository & Service 模式,对 Laravel 中文社区的教程 2 中的 Larabbs 项目使用该模式进行了重构,实际开发过程可以参考其中的分层设计。
larabbs
职责说明
Controller 岗位职责 :
校验是否有必要处理请求,是否有权限和是否请求参数合法等。无权限或不合法请求直接 response 返回格式统一的数据
将校验后的参数或 Request 传入 Service 中具体的方法,安排 Service 实现具体的功能业务逻辑
Controller 中可以通过__construct()
依赖注入多个 Service。比如 UserController
中可能会注入 UserService
(用户相关的功能业务)和 EmailService
(邮件相关的功能业务)
使用统一的 $this->response
调用sucess
或fail
方法来返回统一的数据格式
(可选)使用 Laravel Api Resource 的同学可能在 Controller 中还会有转换数据的逻辑。比如,return Response::success(new UserCollection($resource));
或return Response::success(new UserResource($user));
Service 岗位职责 :
实现项目中的具体功能 业务。所以 Service 中定义的方法名,应该是用来描述功能或业务 的(动词+业务描述)。比如handleListPageDisplay
和handleProfilePageDisplay
,分别对应用户列表展示和用户详情页展示的需求。
处理 Controller 中传入的参数,进行业务判断
3.(可选)根据业务需求配置相应的 Criteria 和 Presenter 后(不需要的可以不用配置,或者将通用的配置到 Repository 中)
调用 Repository 处理数据的逻辑
Service 可以不注入 Repository,或者只注入与处理当前业务存在数据关联 的 Repository。比如,EmailService
中或许就只有调用第三方 API 的逻辑,不需要更新维护系统中的数据,就不需要注入 Repository;OrderService
中实现了订单出库逻辑后,还需要生成相应的财务结算单据,就需要注入 OrderReposoitory
和FinancialDocumentRepository
,财务单据中的原单号关联着订单号,存在着数据关联。
Service 中不允许调用其他 Service,保持职责单一,如有需要,应该考虑 Controller 中调用
Repository 岗位职责 :
只负责数据维护 的逻辑,数据怎么查询、更新、创建、删除,以及相关联 的数据如何维护。所以 Repository 中定义的方法名,应该是用来描述数据是以怎样的形式去维护的 。比如 searchUsersByPage
、searchUsersById
和insertUser
。
Repository 只绑定一个 model,只允许 维护与当前 Repository 绑定的 Model 数据,最多允许 维护与绑定的 Model 存在关联关系的 Model。比如,订单 OrderRepository 中会涉及到更新订单商品 OrderGoodsRepository 的数据。
Repository 中可以配置条件查询(Criteria)、数据校验(Validator)和数据转换显示(Presenter),通常是将通用的配置在 Repository,不通用的独立出相应文件。
Repository 本质是在 Laravel ORM Model 中的一层封装,可以完全不用担心使用 Repository 等同于放弃了 ORM 灵活性。原先常用的 ORM 方法并没有移除 ,只是位置从 Controller 中换到了 Repository 而已。
Repository 中的 $this->model
实际就是绑定的 Model 实例,所以就有了这样的写法$this->model::all()
,与原先的 ORM 写法User::all()
是完全等价的。
Repository 中不允许引入其他 Repository
Model 岗位职责 :
经过前面的 Service 和 Repository 「分层」,剥离了可能存在于 Model 中的很多逻辑,比如校验参数,拼接查询,处理业务和转换数据结构等。所以,现如今的 Model 只需要相对简单地数据定义就可以了。比如,对数据表的定义,字段的映射,以及数据表之间关联关系等,提供给 Repository 中使用就够了。
Repository 模式中涉及到的一些名词理解
完整的执行顺序:Criteria -> Validator -> Presenter
Enums :
这个是 lumen-api-starter 新增的部分,用来定义应用系统中常量的数据。
Criteria :l5-repository criteria
作用类似 Eloquent Model 中的 Scope 查询,把常用的查询提取出来,但是比 Scope 更强大。
可以省去 Model 中大量的根据请求参数判断并拼接查询条件的代码,与此同时,能够做到将多种数据之间存在的通用 筛选条件剥离出来。
比如 make:repository
创建生成的 Repository 中默认包含以下代码,就是给 Repository 默认配置了一个 RequestCriteria,就可以直接使用下面的方式来过滤数据,是不是非常方便?!
public function boot ()
{
$ this ->pushCriteria (app (RequestCriteria ::class));
}
Presenter :L5-repository presenters
可选,使用 Api Resource 的同学可以略过。需要安装 composer require league/fractal
,Dingo Api 中的 transformer 也是使用了这个扩展包。
作用类似 Laravel 的 Api Resource,或者可以说 Api Resource 是 Transformer 的轻量实现。
L5-repository 认为你将数据表结构的数据转换 后是为了用来展示 的,所以它将数据转换相关的逻辑独立出来,称为 Presenter。本质是整合了 fractal 中的 transformer 功能。
Transformer 的优秀之处这里暂不做讨论,因为这里的主角是 Presenter。传送门
先对比一下几种数据转换方式:
Dingo Api 中 transformer 的使用方式
在 Controller 中调用 Response 中的 item 返回数据时传入 transformer 来转换数据
return $ this ->item ($ user , new UserTransformer , ['key' => 'user' ]);
Laravel 中 Api Resource 的使用方式
在 Controller 中调用 Resource 或者 ResourceCollection 转换数据
//return Response::success(new UserResource($user));// 使用 lumen-api-starter 统一 code\status\message\data
return new UserResource ($ user );// 未统一响应结构
L5-repository 中 transformer 的使用方式(为了避免混淆,这里讲的是独立出文件的形式,当然也有可以直接在 model 或 repository 中定义的方式,更详细的使用请参考 l5-repository 的说明)
需要先定义 transformer,然后在 Presenter 中「注册」,最后在调用 Repository 时使用。
举例:
定义 UserTransformer
// app/Repositories/Transformers/UserTransformer.php
<?php
namespace App\Repositories\Transformers;
use App\Repositories\Models\User;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'nickname' => $user->name,
'email' => $user->email,
];
}
}
「注册」到 UserPresenter
// app/Repositories/Presenters/UserPresenter.php
<?php
namespace App \Repositories \Presenters ;
use App \Repositories \Transformers \UserTransformer ;
use League \Fractal \TransformerAbstract ;use Prettus \Repository \Presenter \FractalPresenter ;
class UserPresenter extends FractalPresenter
{
/**
* Prepare data to present
*
* @return TransformerAbstract
*/
public function getTransformer ()
{
return new UserTransformer ();
}
}
在调用 repository 的时候使用
// app/Services/UserService.php
public function listPage (Request $ request )
{
$ this ->repository ->pushCriteria (new UserCriteria ($ request ));
$ this ->repository ->setPresenter (UserPresenter ::class);
return $ this ->repository ->searchUsersByPage ();
}
看得出 Dingo Api 和 Api Resource 都是在最后响应数据的环节来转换数据,而 Repository 模式中认为但凡是与数据有关的处理逻辑都应该被「装进 Repository中」 ,应用系统中的其他部分不需要关心数据如何去查询(Criteria),如何去校验(Validator),以及如何去转换后提供显示(Presenter)。其他部分做好相应的职责就行,但凡与数据打交道的地方都交给 Repository。
规范
controller:
类名:名词,复数形式,描述是对整个资源集合进行操作;当没有集合概念的时候。换句话说,当资源只有一个的情况下,使用单数资源名称也是可以的——即一个单一的资源。例如,如果有一个单一的总体配置资源,你可以使用一个单数名称来表示
方法名:动词+名词,体现资源操作。如,store\destroy
service:
类名:名词,单数。比如UserService
、EmailService
和OrderService
方法名:动词+名词
,描述能够实现的业务需求。比如:handleRegistration
表示实现用户注册功能。
repository
类名:名词,单数。make:repository
命令可以直接生成。
方法名:动词+名词,描述数据的维护(CRUD)。 比如:searchUsersByPage
可能会出现的动词:createXXX(add);searchXXX;queryXXX、findXXX、fetch(get);updateXXX;deleteXXX(destroy);组合形式:with\Join...,如 searchOrdersLeftJoinGodds
通常情况 Database、Cache、Redis、Carbon 操作只能出现在 repository
其他
依照惯例,如对您的日常工作有所帮助或启发,欢迎三连 star + fork + follow
。
参考
License
The Lumen Api Starter is open-sourced software licensed under the MIT license .
请发表评论