# 开发规范
# 项目规范
# 开发和线上环境
一般情况下,一个项目应该有三个基本的项目环境:
- Local - 开发环境
统一使用域名 .test
作为后缀。
- Staging - 线上测试环境
统一使用域名 .stage.xxx
作为后缀。
除了域名等其他独立应用配置以外,环境必须跟 Production 保持高度一致性,可以的话应该与 Production 使用同一台机器。
- Production - 线上生产环境
出于安全考虑,线上环境 必须 只开放以下端口:
- 80 HTTP
- 443 HTTPS
- 22 SSH
# 工具统一
工具统一,为了方便工作流的统一,还有工具使用经验的传承。
- 硬件系统
- 编辑器
- 编辑器代码格式化 EditorConfig (opens new window)
- PHP 代码风格矫正器 PHP-CS-Fixer (opens new window)
- 命令行工具 例如:xshell 、git for windows
- 浏览器
- 虚拟机
- Mysql 数据库查询工具
- Redis 管理工具
- MongoDB 管理工具
- 设计工具
- 视频播放
- zsh 的配置信息统一,Alias 等信息
# 项目文档编写规范
每一个项目必须包含一个 README.md 文件,主要简单介绍该项目,作用主要有两个:一是团队新成员可以从此文件快速获悉项目大致情况,另一个是部署项目时可以做为参考。
readme.md
文件应该包含以下内容:
- 项目概述:介绍说明项目的一些情况,例如产品说明,功能描述,相关链接等等
- 运行环境:运行环境说明,系统要求等信息
- 开发环境部署:一步步引导说明,保证新成员能最快速的,没有歧义的部署好开发环境
- 服务器架构说明:最好能有服务器架构图,包含前端和后端运行环境等
- 代码上线:介绍代码上线流程,需要执行那些步骤
- 扩展包说明:表格列出所有使用的扩展包,以及标注那些业务使用了相关扩展包
- 自定义 Artisan 命令列表:以表格的形式罗列出所有自定义的命令,说明用途和使用场景
- 队列列表:以表格的形式罗列出所有的队列接口,说明用途,指出使用场景
- 常见问题:如果遇到了什么问题,如何解决的问题
# Composer 使用须知
一些扩展包的注册会对应用造成消耗,有一些扩展包事开发环境中专用,生产环境中并不会使用到,为了避免无用的负载,必须严格控制其安装和加载。
安装开发专用扩展包时,必须使用 --dev
参数
composer require ext-name --dev
在 Production 和 Staging 环境,必须使用以下命令来安装
composer require --no-dev
# 静态资源
一般我们将 css,js,font,image 等称为静态资源,这些静态资源使用 cdn 加载,用户上传的静态资源应该酌情处理。
如果项目中使用了第三方前端类库中的 css/js/fonts/image 等静态资源,绝不使用第三方链接进行加载,必须下载下来并纳入代码版本管理器中。
css,js 文件应该使用独立的文件进行加载,不应该直接写在页面中,并且控制数量为 2~3 个文件。
# 开发规范
# 配置信息与环境变量
由于 .env
不会被纳入版本控制器中,所以本地 .env
里添加变量时必须同步到 .env.example
中,以免影响其他项目成员。
所有程序配置信息,必须通过 config()
来读取,所有的 .env
配置信息必须通过 env()
来读取,绝不在配置文件以外的范围使用 env()
。这样做有以下优势:
1、定义分明,config()
是配置信息,env()
是环境变量
2、统一放置于 config()
,可以使用配置信息缓存功能来提高运行效率
3、代码健壮性,config()
是在 env()
之上多出来一个抽象层,会使代码更加健壮和灵活
# 控制器
控制器必须优先使用 Restful 资源控制器
必须使用复数形式
控制器里应该只存放路由动作方法
保持短小精悍,必须保持控制其文件代码行数最小化,还有可读性,一般来讲,一个方法不应该超过20行代码,业务逻辑比较多,请封装到一个 Service 类里。
绝不在控制器里遗留死方法(没有用的方法)
不应该在控制器中书写私有方法,多余的业务逻辑,请封装到 Service 类中
方法注释,每个方法都需要有详细的注释说明(要求方法取名合理,不需要过多解释),包括参数和返回值。但要为一些复杂的逻辑代码块书写注释,结合上下文,解释为什么这么做。
请注意,@param 属性后跟两个空格、参数类型、两个空格,最后是变量名称:
/** * 注册一个绑定到容器。 * * @param string|array $abstract * @param \Closure|string|null $concrete * @param bool $shared * @return void */ public function bind($abstract, $concrete = null, $shared = false) { // }
绝不在控制器里批量注释掉代码,没有用直接删掉,反正有 Git 来做版本控制
# 路由器
- 绝不在路由配置文件中写闭包路由或者其他业务逻辑代码,这样无法使用路由缓存。
- 必须优先使用 restful 路由。
- 使用资源路由时,如果仅使用到部分路由,必须使用 only 列出所有可用路由。
- 获取 URL 必须 遵循以下优先级:
- $model->link()
- route 方法
- url 方法
# 视图
避免在 resources/views
目录下直接放置视图文件
统一布局,相似的页面,局部视图,必须使用 layouts 文件来统一页面头部与尾部
优先使用
blade
模板引擎保持目录清晰,蛇形小写复数命名形式
- layouts 存放布局文件
- common 存放页面通用元素
- components 存放组件
- pages 存放简单的页面
- partials 存放部分模板片段
- resources 对应资源路由名称
局部视图文件必须使用
_
前缀来命名视图命名要释义,例如 index create show edit
共用
_form.blade.php
视图,在很多情况下,创建和编辑视图页面结构相似,应该避免重复代码
# 模型
# 命名规范
数据模型相关的命名规范:
- 数据模型类名 必须 为「单数」, 如:App\Models\Photo
- 类文件名 必须 为「单数」,如:app/Models/Photo.php
- 数据库表名字 必须 为「复数」,多个单词情况下使用「Snake Case」 如:photos, my_photos
- 数据库表迁移名字 必须 为「复数」,如:2014_08_08_234417_create_photos_table.php
- 数据填充文件名 必须 为「复数」,如:PhotosTableSeeder.php
- 数据库字段名 必须 为「Snake Case」,如:view_count, is_vip
- 数据库表主键 必须 为「id」
- 数据库表外键 必须 为「resource_id」,如:user_id, post_id
- 数据模型变量 必须 为「resource_id」,如:$user_id, $post_id
总的来讲,就是只有表名和数据填充文件是复数,其他情况都是单数。
# 利用 trait 来扩展数据模型
模型之间,相同的模型逻辑,应该 利用 Trait 来实现逻辑代码。所有模型 Traits 必须存放于 app/Models/Traits 目录下。业务逻辑请使用 ModelService 模式来组织代码。
# 关于 sql 文件
强烈建议使用数据迁移,比如创建数据库表,更改字段,创建索引,测试数据等行为。
# 数据层无状态
数据层,也就是模型层,不能跟用户的登录状态挂钩
# 目录分层
如果是一个长期维护的项目,必须为模型文件按业务逻辑分层
# 模型事件
应该尽量避免使用 laravel 的模型事件,使用模型事件的问题在于,其职能很难界定,所有的业务逻辑都能写到模型事件中。模型事件另一个缺点就是,模型操作,附带太多逻辑,有太多的 Side Effect,并且是隐性的。
# Service模式
项目中的大部分业务逻辑,都应该封装到 Service
层,这不仅能更好的组织代码。还方便单元测试。控制器方法只处理请求逻辑,模型只处理模型定义,以及数据关联逻辑。业务逻辑必须封装到对应的 ModelService
类中
- ModelService 的方法命名参照模型的方法
- 其他类型的类,都应该使用 Service 来封装,例如:请求第三方接口的类,图片处理的工具类,包含业务逻辑的类
- 所有的 Service 类必须放在
app\Services
目录中,可以根据业务逻辑将其目录分层 - Service 方法无状态,这就意味着无论在控制器方法,命令行,测试代码中,都可以调用
# 表单验证
必须使用表单请求类来处理控制器里的表单验证
绝不使用 authorize()
方法来做用户授权,用户授权单独使用授权策略来实现
所有 FormRequest 表验证类必须继承 app/Http/Requests/Request.php 基类。
FormRequest 表验证类命名必须按照控制器方法来命名,且必须添加模型的前缀,类名称的 Request 后缀也是必须的。
# 中间件
Auth 中间件必须写在控制器的 __construct
方法中,并且必须使用 except 黑名单进行过滤,这样当你新增控制器方法时,默认是安全的。
# 授权策略
- 授权相关逻辑,必须使用授权策略类来实现。
- 授权策略类命名必须以 Policy 结尾,遵循资源路由单数形式进行命名。
- 应该使用自动发现授权策略方法
- 所有的授权策略类必须继承
App\Policies\Policy
基类。
<?php
namespace App\Policies;
use Illuminate\Auth\Access\HandlesAuthorization;
class Policy
{
use HandlesAuthorization;
public function __construct()
{
//
}
public function before($user, $ability)
{
if ($user->isAdmin()) {
return true;
}
}
}
# 数据填充
- 文件名需要按照驼峰式来命名,并且严格遵守大小写规范,例如
UsersTableSeeder
- 必须使用
factory
方法来做数据填充, - 另外需要注意的是运行效率,keep it lighting speed.
所有的假数据入库操作,都必须使用批量操作。
$users = User::factory()->times(1000)->make();
User::insert($users->toArray());