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方法,验证码下发使用了队列,可以提升下发效率

主要使用

  1. schedule 关于schedule的写法
  2. mysql 关于数据库表、字段的命名原则
  3. showdoc 关于接口文档自动生成
  4. queue 关于队列
  5. GLogger 关于日志管理
  6. AES 关于接口加密
  7. file文件的处理 关于文件的处理
  8. RBAC的权限控制 RBAC的权限控制(待补充)
  9. 一些工具 一些工具
  10. 一些写法 一些写法
  11. env说明env说明
  12. 公共的方法公共的方法
Last Updated:
Contributors: mtt-Ada@yisa.art