Guide
gcoder是团队整理的PHP Laravel项目开发框架,其中集成了: 快速生成代码,Model->Manager->(Service & Trait)->Controller,以及api和web的路由 关于V2版本 By 海波 关于Schedule的写法 By 海波 鉴权机制,token放置在header中 缓存设置,通过env的配置设置 加密选项,进行接口加密 统一异常管理 RBAC的权限控制 接口格式封装,ApiResponse 一些常用的SDK 一些标准模块的样例,例如用户注册、登录、下发验证码等 其他 gcoder可以说是团队智慧的结晶,可以较好地提升项目开发效率和规范性,每一个初入团队的小伙伴,都应该学习gcoder的使用,以便快速进入状态,参与项目开发协作。
版本计划
原gcoder版本均使用静态方法,没有使用面向对象的编程,但比较适合于当前团队的项目执行情况。
新版的gcoder仍旧没有使用类和对象的方法,究其原因,一切架构都服务于业务,如果我们的业务庞大到要有较好的架构来支持,那么建议使用SpringBoot/SpringCloud的框架,200万用户以上,PHP适合于中小型项目(通过模块化的管理也可以支持适当的中大型项目)的开发,随着项目的发展,代码重构是必不可少的工作。
gcoder的初衷
gcoder的初衷是提升效率、提升规范性、减低开发难度,开发人员只要有基础的开发能力就可以有效地参与项目开发工作。 TIPS:多年的项目实施经验告诉我们,代码只是工具,重要的是思维,那么要求后端人员具备的基本思维能力是抽象思考能力(数据模型)、流程化的思维能力(业务流程实现),再有就是考虑问题的全面性(接口的健壮性、规范性、安全性)等等
系统要求
请注意,本次gcoder使用laravel 7的框架,要求PHP 版本为7.4版本以上,在实际项目中,并不建议使用laravel7的版本,主要原因是: 宝塔不支持PHP 7.4的扩展管理,自己安装扩展太麻烦,建议还是使用7.2的版本 一些扩展包是基于laravel 5.8框架的,所以从生态角度来讲,目前还是laravel 5.8更适合项目的开发工作
主要引用
- laravel
- 数据库
- showdoc接口文档工具
- GLogger日志管理
- schedule定时任务
gcoder的使用方法
修改.env文件,连接至项目数据库 #数据库连接 mysql-测试 DB_CONNECTION=mysql DB_HOST=152.136.115.180 DB_PORT=3306 DB_DATABASE= DB_USERNAME= DB_PASSWORD= 运行php artisan auto:createFiles命令,将在storage的app/code目录下生成相关代码 请注意,不要在gcoder里面进行项目开发,请将生成的代码copy到目标项目中,然后全文替换一些路径
生成的文件路径
生成的代码放置在storage/app/Code下,其中要求所有的Laravel项目必须使用分模块的方案实现,模块的命名规则为:
xxxPortal:UserPortal、AdminPortal为Api接口的模块,即其中暴露的都是Api,例如未来有门店Api,则命名为ShopPortal
xxxConsole:AdminConsole为blade模板,即blade模板
具体文件夹为:
Controllers:AdminConsole、AdminPortal、UserPortal,主要职责为接收参数、参数校验/转换、返回结果
DB:数据库建立默认索引,主要是status和sort
Managers:Managers层数据库CURD的封装
Models:模型文件
Route:路由文件,分别映射Controllers的文件夹
Traits:V2版本添加,主要用于Managers的扩展(非必要)
V2版本的修改
- V2版本与原版本并不冲突,原版本保持不变
- 新版的代码生成命令:php artisan auto:createFilesV2
- 代码生成在之前版本目录的v2文件夹下,Traits除外
├─storage
│ └─app
│ └─code
│ └─Controllers
│ └─AdminConsole
│ ├─V2
│ └─AdminConsole
│ ├─V2
│ └─...
│ └─Managers
│ └─MySQL
│ ├─V2
│ └─...
│ └─raits
- 传递数组的优化,再往版本中前端传递的数组类型都是都是','分割的字符串,后端处理成数组。以后前端传递的就是数组类型的参数。
- 数据库枚举型字段的映射xxx_type、xxx_status等以后直接写在Model中,以往的Project随着项目的增长,将变得难以维护。但是公共status字段无需改动。
- 优化了Manager层getListByCon方法、setInfo方法; 以往我们在对数据库字段做增、删、改时需要同时对这两个方法一并进行修改。 现在manager添加了静态属性,专门维护数据库字段。无需改动这个两个方法,同时也减少了代码行可读性更高
// 数据表全列数组,数据库增删字段时需要手动操作这里
protected static $table_columns = [
'id',
'f_table',
// ...
];
// ********* getListByCon start *********
// 根据传入的字段条件,拼装where条件(必须数据库中有的字段),对每次数据库字段的增删不需要调整这里
foreach ($con_arr as $column => $values) {
if (in_array($column, self::$table_columns)) {
// 如果values不是数组,认定等号操作
if (!is_array($values)) {
$infos = $infos->where($column, $values);
} else {
// 非等于的查询
if (isset($values['operator']) && isset($values['value'])) {
switch ($values['operator']) {
// 可自行补充null、not_null、exists、not_exists等,默认=、<=、>=、<>
default :
$infos = $infos->where($column, $values['operator'], $values['value']);
break;
case 'in' :
$infos = $infos->whereIn($column, $values['value']);
break;
}
}
}
}
}
// 特殊的,例如需要调用闭包函数的需单独写,con_arr 中的字段名不能与数据库字段名重复
if (array_key_exists('search_word', $con_arr) && !Utils::isObjNull($con_arr['search_word'])) {
$keyword = $con_arr['search_word'];
$infos = $infos->where(function ($query) use ($keyword) {
$query->where('name', 'like', "%{$keyword}%")
->orwhere('id', 'like', "%{$keyword}%");
});
}
// 特殊的,例如年月日等日期的转换需单独写,year、month、day, con_arr 中的字段名不能与数据库字段名重复
if (array_key_exists('gte_created_at_date', $con_arr) && !Utils::isObjNull($con_arr['gte_created_at_date'])) {
$infos = $infos->whereDate('created_at', '>=', $con_arr['gte_created_at_date']);
}
// ********* getListByCon *********
// ********* setInfo start *********
// 对每次数据库字段的增删不需要调整这里
foreach ($data as $column => $value) {
if (in_array($column, self::$table_columns) && !Utils::isObjNull($value)) {
$info->{$column} = $value;
}
}
// ********* setInfo end *********
- 新增了Traits层,它是Manager层的扩展;在实际业务中manager的提供的方法还远远不够,我们会在manager添加很多方法, 虽然奇哥新增了service层,但是manager层还是非常臃肿,为了减少manager层的代码行数,利用了trait特性来扩展manager, 所有的衍生方法写在Traits中,manager中use该trait,在控制器或Service中还可利用manager静态调用Traits的方法,非必要可不用。
- 对删除数据做了调整,在之前的版本中删除数据,有单个删除deleteById、批量删除bathDelete。现在统一改成了destroy,支持int型和array型的参数。
- 新增根据条件删除数据方法deleteByCon。涉及到缓存,建议使用
- 新增根据条件更新数据方法updateByCon。涉及到缓存,建议使用
- 新增根据不同id批量修改同一字段的不同值方法batchUpdateByColumn。以往遇到这种情况大家都是利用循环去更新数据库,这么做增加了数据库的连接次数浪费服务器资源。 现在,利用循环处理数据,直接调用此方法
$update_data = [];
foreach ($models as $k => $model) {
$k ++;
$arr = [
'id' => $model->id,
'sort' => $k,
];
array_push($update_data, $arr);
}
XXXManager::batchUpdateByColumn($update_data);
这么做的好处是,虽然利用了循环但是只做了一次数据库操作
- 关于图片存储,在以后的项目中建议单张图片存储字段用img,多张图片字段用images_json,前端处理完数据后直接插入数据库无需额外的处理
- 优化setNum方法。
/**
* 统一封装数量操作,部分对象涉及数量操作,例如产品销售,剩余数等,统一通过该方法封装
*
* @param object $info model对象
* 一般都是在controller查询后进行计算,此处直接传递对象,无需重复查询
* @param string $column 操作字段
* @param int num 加减数值,有符号,加正号、减负号
* @return object|bool
*/
public static function setNum($info, $column, $num)
{
// 必须是数据库中的字段,info必须是model对象
if (!in_array($column, self::$table_columns) || $info == null) {
return false;
}
// 如是负数此方法可正常加减
$info->increment($column, $num);
// 如果开启缓存,则应该把值存入缓存
if (env('ENABLE_MODEL_CACHE', false)) {
$key = "{{$var_name}}:" . $info->id;
GLogger::processLog(__METHOD__, "开启Model缓存,删除数据需要同步删除缓存中的数据,缓存的key:" . $key, GLogger::LOG_DEBUG);
CacheManager::forget($key);
}
return $info;
}
项目结构
本次进行了代码层次的抽象,本质上来说,与以往的项目结构一致,即:
- Controller:作为最外层,职责为使用RequestValidator进行参数格式的校验,并返回数据
- Service:新增了Service层,将原来的Manager层进行了拆分,即有一些需要事务(Transcation)的代码,要封装在Service中,Service即对上层的Controller提供业务支持,不是要求全部的Controller都通过Service来支持,但是如果在复杂的业务场景下,就需要由Service来封装服务,支持上层业务
- Manager:基础的数据库CURD类,通过该方法,可以有效地进行根据条件查询、save等方法,在Manager层中,进行了缓存的改造,可以通过ENABLE_MODEL_CACHE进行开启Model层的缓存,缓存getById和getByIdWithTrash方法
- Model:在Model层,没有过多的变化,根据分析,仍不建议再Model层写过多的逻辑,但有些基础能力要在Model层使用,例如json的转换(一些字段建议存储为json)、数据填充和默认的加密存储(数据库层面)的加密存储
此外,Commponents是定义的组件库,其中Common文件夹下是标准化的文件方法,针对于Common文件夹下的方法:
- ApiResponse:标准的响应方法,目前响应报文中的数据格式为code、data和msg,没有针对接口的幂等性进行处理
- DateTool:日期函数,一般也可以使用Carbon来进行日期的处理,但是仍旧不是很语义话
- GLogger:日志函数,针对日志统一进行管理
- RequestValidator:参数校验函数
- Utils:公共的工具类
其他的SDK为封装的一些三方SDK方法,Project.php为项目自身的方法和一些常量的定义
验证码下发
- 在app/Http/Controllers/CommonController中,sendVerifyCode方法,验证码下发使用了队列,可以提升下发效率