不要造轮子
业务推进不应该耗在轮子制造上
- 典型、常见的功能首先考虑
Laravel框架
是否已有实现,其次搜索开源的轮子
Composer
兴起:PHP
进入现代化开发阶段、大家可以共享优秀的轮子- 使用现成的轮子
- 框架内置的轮子:适度定制满足业务,以后框架升级最为方便
- 大牛开源的轮子:功能丰富、架构规范、拓展性强、文档完善、持续维护!!
现成轮子不满足部分需求或有Bug?
- 不要直接修改!!
vendor
目录下框架内部组件
或第三方包
- 造成包无法进行后续更新
Compser
重装vendor
包后,定制修改将会丢失
- 新建逻辑分层进行定制(比如
Libraries
目录)- 目录结构组织参照
vendor
下的原始实现类 extends
原始实现类覆盖需要定制的方法,或者直接implements
其整个Constract
约定- 在业务逻辑中(而不是
vendor
目录)替换为自定义类
- 目录结构组织参照
几个轮子参考站点
Laravel专属包搜索
:Composer官方搜索
:Github搜索
:Laravel一些开源项目
:Spatie开源的Laravel项目
:PHP优秀包联盟
:
Laravel主流分层思想
系统架构需要逻辑分层,每个分层遵循单一职责原则
Web分层
Controller
层作为业务逻辑的总调度,可注入Service
、Repository
等Service
层辅助Controller
层,抽象复杂业务逻辑、抽象公共服务、处理第三方功能调用等Repository
层具体处理CURD
的SQL业务
、第三方数据调用等资源操作逻辑,可注入Model
- 需要面向接口开发, 依照接口声明处理 DB、Cache、Api等实现
Model
层作为业务实体,处理常量定义
、关联关系
、配置定义
View
层处理视图展现
Api分层
1. 业务:Dingo路由 + Controller调度 + Service业务逻辑 -> Transformer数据修饰输出2. 数据逻辑:Query过滤参数&Repository查询 + Hydrator数据校验并存储 -> Policy鉴权策略3. 数据:Model(域、常量、关联) + Migration、Factory、Seeder4. 配置:Config配置 > ENV环境变量*. 其它:Test(状态码、数据结构、过滤条件)
Laraval权限分层
1. 顶层基于Auth中间件的用户身份认证2. 主体**基于角色**的RBAC权限架构设计 - 基于角色分类组织权限节点 - 决定某个角色能使用什么功能、能做什么操作3. 底层**基于用户**的鉴权 * 基于Gate的资源无关操作鉴权 - 鉴权用户能否某种操作,一般与资源实例无关 - 定义了单一权限节点的鉴权逻辑 * 基于Policy的Model实例资源鉴权 - 鉴权用户能否对Model实例资源进行某种操作 - 封装资源的多个权限节点鉴权逻辑于一个策略中,并与Model绑定
错误和异常处理
使用异常的好处
- 不用针对正常、错误的返回而使用类似
[status=>sucees, data => $data]
这种结构 - 业务中引入异常体系后,逻辑更加语义化
异常处理原则
- 业务设计之内的异常,自己抛出自己捕获处理
- 正常场景就返回数据, 错误场景就抛出异常
- 抛出的异常在外层业务进行捕获并进一步处理
- 业务设计以外的异常, 自己未能捕获,冒泡至
Handler
顶层异常处理器处理 - 异常基类
- 需要显示的异常继承自
\Symfony\Component\HttpKernel\Exception\HttpException
- 业务逻辑中组织的异常继承自
\Exception、\RuntimeException等
- 需要显示的异常继承自
错误日志系统
- 典型的报错日志系统(有缺陷)
- 日志源抓取
Laravel StorageLog
、PHP error.log
、Nginx error.log
- 只提供错误点的调用栈信息
- 缺少当时场景的关键信息,调试排错困难
- 日志源抓取
- 使用
Laravel
的顶层异常处理器
衔接报错日志系统
(推荐)PHP7
下所有Error
和Exception
都是Throwalbe
接口的实现,均可被Catch
report()
方法中进行异常上报可实现当时场景
的关键上下文信息
串联,提高排查效率- 当时的用户对象
- 当时完整的用户请求数据
- 当时的异常堆栈信息
依赖注入
自动依赖注入 常用于容器自动注入空Model实体对象
、业务逻辑模块
等
- 构造函数注入:
public function __construct(User $user)
- 接口注入
- 控制器方法中:
public function index(Request $request)
- 队列
Job
的handle()
方法中:public function handle(MailService $mailService)
- 等等
- 控制器方法中:
手工依赖注入 常用于手工注入具化Model实体对象
、同接口下 不同的业务模块实现
等
- Set注入:
public function setUser(User $user);
使用场景区别举例
Repository
层的工作依赖于Model实体
,不同场景下使用不同的注入方式 - 查询场景下 -
自动依赖注入
- 通过
构造函数
来注入空Model实体
- 通过
空Model实体
发起newQuery
进行查询
- 通过
- 更新场景下 -
手工依赖注入
- 通过
Setter
来注入具化Model实体
或id
到Repository
- 注入的
具化Model实体
将作为数据更新目标
集成测试
- 即便做不到TDD,至少粗粒度上的集成测试来保证接口可用性,避免人工测试的困难
- 核心逻辑模块可以考虑更细粒度的单元测试
1. 指定测试方法 `phpunit --filter clsTest::testMethod`2. 测试临时忽略指定检查,添加注解 @SupressWarnings("规则名") 3. 测试覆盖范围 - 常规场景数据返回: 数据 & 结构 & 状态码 - 异常场景数据返回:数据不存在 - 所有过滤字段 - 分页功能:指定分页 & 单页条数 - 数据录入 & 更新 正常
遇过的坑
-
连不上mysql容器实例,找不到host,无法完成migrate迁移操作
需要在宿主机下做哥容器hostname的host解析 -
factory数据异常 考虑重启docker容器
-
factory中faker一个datetime数据时, 留意给出format格式
-
进入tinker交互式环境后代码更新没有及时展现出来
tinker将以进入环境时内存中的代码逻辑来运转,后期代码的更新不会在tinker环境中生效 -
删除某个文件后, 程序运行报错类不存在
删除composer缓存文件,composer dumpautoload --optimize -
集成测试时的测试数据准备
需要留意, 除了主体测试数据需要准备外,还需要准备关联数据 -
报错调试
查看报错信息一定从框架自带日志着手, 因为报错细节已被框架截获转发到自带日志上, php日志及程序返回中已抹除报错细节 -
Transformer里留意数字类型字段输出的强制类型转换
-
如果migration操作涉及到SQLite驱动,则务必将每个字段调整封装进单独一个Schema闭包中,否则将出现各种异常
-
宿主机测试通过,容器内不通过, 错误提示信息是类不存在。
- 检查命名空间
- 检查文件名路径
PHPStorm
- 安装
Laravel Plugin
,并在项目中启用(每个项目都得单独启用) - Settings->Directories中标示app为Sources目录, 指定其Package Prefix为App
- Alt+Insert快捷键 自动生成代码
- Alt+Enter快捷键 智能补全注释
- Ctl+MouseOver快捷键 函数提示