基于TP实现的基本业务代码生成器

是否厌倦了基本业务的增删改查,仔细想想,每次业务基本都是一样的。都是根据一个数据表来生成对应的控制器、模型、DTO、service、验证器。所以自己对日常的代码进行了高度抽象,通过正则表达式,自动生成了对应的代码文件及其注释。本功能结合tp的command类实现。

代码实现:

  1. AutoCode 类
checkSQL($tableSQL);
        $autoCodeParam = new AutoCodeParam();
        $autoCodeParam->tableSQL = $tableSQL;

        //匹配表的名字
        //CREATE TABLE `dbase_banner`
        if (preg_match('/^(.*?CREATE\s*TABLE\s*)`(.*?)`.*\(/', $tableSQL, $m)) {
            $autoCodeParam->tableName = $m[2];
        } else {
            exception("没有扫描到表的名字");
        }
        //扫描主键,没有的扫描到用id
        if (preg_match('/PRIMARY\s*KEY\s*\(`(.*?)`\)/', $tableSQL, $m1)) {
            $autoCodeParam->pkName = $m1[1];
        }
        if (preg_match('/COMMENT=\'(.*?)\';$/', $tableSQL, $m2)) {
            $autoCodeParam->tableDesc = $m2[1];
        }
        return $autoCodeParam;
    }

    protected function checkSQL($tableSQL)
    {
        //检查是否满足条件,必须全部加注释
        preg_match_all('/^\s*`.*/m', $tableSQL, $lines);
        preg_match_all('/^\s*`.*?COMMENT.*?/m', $tableSQL, $comments);
        if (count($lines) != count($comments)) {
            exception("SQL创建表有些字段缺少注释,不能生成");
        }
        return true;
    }

    protected function checkFile(AutoCodeParam $param)
    {

        //检查文件是否存在
        return true;
    }


    /**
     * 生成接口传参文档
     * @param AutoCodeParam $param
     * @return string
     */
    public static function createDoc(AutoCodeParam $param)
    {
        $tableSql = $param->tableSQL;
        $matches = [];
        preg_match_all("#`(.*?)`(.*?) COMMENT\s*'(.*?)',#", $tableSql, $matches);
        $fields = $matches[1];
        $types = $matches[2];
        $comments = $matches[3];
        $doc = "参数名称 | 参数类型 | 是否必填 | 说明
:--- | :---: | :---: | :---
";
        for ($i = 0; $i < count($matches[0]); $i++) {
            $field = $fields[$i];
            $type = strpos($types[$i], 'int') !== false ? 'Number' : 'String';
            $must = "N";
            $comment = $comments[$i];
            $doc .= $field . " | " . $type . " | " . $must . " | " . $comment . "\n";

        }
        return $doc;
    }


    /**
     * 生成服务器返回结果文档
     * @param AutoCodeParam $param
     * @return string
     */
    public static function createResDoc(AutoCodeParam $param)
    {
        $tableSql = $param->tableSQL;
        $matches = [];
        preg_match_all("#`(.*?)`(.*?) COMMENT\s*'(.*?)',#", $tableSql, $matches);
        $fields = $matches[1];
        $types = $matches[2];
        $comments = $matches[3];
        $doc = "参数名称 | 参数类型  | 说明
:--- | :---: | :---
";
        for ($i = 0; $i < count($matches[0]); $i++) {
            $field = $fields[$i];
            $type = strpos($types[$i], 'int') !== false ? 'Number' : 'String';

            $comment = $comments[$i];
            $doc .= $field . " | " . $type . " | " . $comment . "\n";

        }
        return $doc;
    }


    public static function createDTO(AutoCodeParam $param)
    {
        $templateFile = __DIR__ . "/templates/DTO.html";
        if (!is_file($templateFile)) {
            exception("DTO模板文件不存在");
        }
        $tableSql = $param->tableSQL;
        $nameSpace = $param->getNameSpace() . "\\logic";
        $matches = [];
        preg_match_all("#`(.*?)`(.*?) COMMENT\s*'(.*?)',#", $tableSql, $matches);
        $defaults = $matches[2];
        $fields = $matches[1];
        $comments = $matches[3];

        $classBody = "\n";
        $tabs = "   ";
        for ($i = 0; $i < count($matches[0]); $i++) {

            $docComment = "$tabs/**\n";
            $comment = $comments[$i];
            //注释
            if (!empty($comment)) {
                $docComment .= "$tabs * {$comment}\n";
            }
            $docComment .= "$tabs * @var ";
            //数据类型
            if (strpos($defaults[$i], 'char') !== false || strpos($defaults[$i], 'text') !== false) {
                $type = "string";
            } elseif (strpos($defaults[$i], 'int') !== false) {
                $type = "int";
            } else {
                $type = '';
            }
            $docComment .= $type . "\n" . "$tabs */\n";

            $field = "{$tabs}public $" . $fields[$i] . ";\n";
            $classBody .= $docComment . $field;
        }

        $varMaps = [
            "nameSpace" => $nameSpace,
            'className' => $param->getDtoName(),
            'classBody' => $classBody,
            'time' => date("H:i:s"),
            'date' => date("Y/m/d")
        ];

        $content = file_get_contents($templateFile);
        $content = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($varMaps) {
            return $varMaps[$matches[1]]??"模板未定义";
        }, $content);
        return $content;
    }


    /**
     * 创建模型
     * @param AutoCodeParam $param
     * @return bool|mixed|string
     */
    public static function createModel(AutoCodeParam $param)
    {
        $templateFile = __DIR__ . "/templates/Model.html";
        if (!is_file($templateFile)) {
            exception("模型模板文件不存在");
        }
        $nameSpace = $param->getNameSpace() . "\\model";
        $varMaps = [
            "nameSpace" => $nameSpace,
            'className' => $param->getModelName(),
            'tableName' => $param->tableName,
            'pk' => $param->pkName,
            'time' => date("H:i:s"),
            'date' => date("Y/m/d")
        ];
        $content = file_get_contents($templateFile);
        $content = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($varMaps) {
            return $varMaps[$matches[1]]??"模板未定义";
        }, $content);
        return $content;
    }


    /**
     * 创建验证器
     * @param AutoCodeParam $param
     * @return bool|mixed|string
     */
    public static function createValidate(AutoCodeParam $param)
    {
        $templateFile = __DIR__ . "/templates/Validate.html";
        if (!is_file($templateFile)) {
            exception("模型模板文件不存在");
        }

        $tabs = "        ";
        $nameSpace = $param->getNameSpace() . "\\validate";
        $matches = [];
        preg_match_all("#`(.*?)`(.*?) COMMENT\s*'(.*?)',#", $param->tableSQL, $matches);
        $fields = $matches[1];
        $comments = $matches[3];
        $doc = "";
        for ($i = 0; $i < count($matches[0]); $i++) {
            $field = $fields[$i];
            $comment = $comments[$i];
            $doc .= $tabs . "'$field|$comment'" . " => 'require'," . "\n";
        }
        $varMaps = [
            "nameSpace" => $nameSpace,
            'className' => $param->getValidateName(),
            'tableName' => $param->tableName,
            'pk' => $param->pkName,
            'time' => date("H:i:s"),
            'date' => date("Y/m/d"),
            'rules' => $doc,
            'param_desc' => self::createDoc($param),
            'controller' => $param->getControllerName(),
            'module' => $param->module,
            'tableDesc' => $param->tableDesc
        ];

        $content = file_get_contents($templateFile);
        $content = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($varMaps) {
            return $varMaps[$matches[1]]??"模板未定义";
        }, $content);
        return $content;
    }

    /**
     * 创建控制器
     * @param AutoCodeParam $param
     * @return bool|mixed|string
     */
    public static function createController(AutoCodeParam $param)
    {
        $templateFile = __DIR__ . "/templates/Controller.html";
        if (!is_file($templateFile)) {
            exception("控制器模板文件不存在");
        }

        $nameSpace = $param->getNameSpace() . "\\controller\\admin";
        $varMaps = [
            "nameSpace" => $nameSpace,
            'className' => $param->getControllerName(),
            'tableDesc' => $param->tableDesc,
            'pk' => $param->pkName,
            'time' => date("H:i:s"),
            'date' => date("Y/m/d"),
            'validateNameSpace' => $param->getNameSpace() . "\\validate\\" . $param->getValidateName(),
            'dtoNameSpace' => $param->getNameSpace() . "\\logic\\" . $param->getDtoName(),
            'serviceNameSpace' => $param->getNameSpace() . "\\service\\" . $param->getServiceName(),
            'serviceClassName' => $param->getServiceName(),
            'validateClassName' => $param->getValidateName(),
            'dtoClassName' => $param->getDtoName(),
        ];
        $content = file_get_contents($templateFile);
        $content = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($varMaps) {
            return $varMaps[$matches[1]]??"模板未定义";
        }, $content);
        return $content;
    }


    /**
     * 创建service
     * @param AutoCodeParam $param
     * @return bool|mixed|string
     */
    public static function createService(AutoCodeParam $param)
    {
        $templateFile = __DIR__ . "/templates/Service.html";
        if (!is_file($templateFile)) {
            exception("控制器模板文件不存在");
        }
        $nameSpace = $param->getNameSpace() . "\\service";
        $varMaps = [
            "nameSpace" => $nameSpace,
            'className' => $param->getServiceName(),
            'tableDesc' => $param->tableDesc,
            'time' => date("H:i:s"),
            'date' => date("Y/m/d"),
            'dtoNameSpace' => $param->getNameSpace() . "\\logic\\" . $param->getDtoName(),
            'modelNameSpace' => $param->getNameSpace() . "\\model\\" . $param->getModelName(),
            'serviceClassName' => $param->getServiceName(),
            'dtoClassName' => $param->getDtoName(),
            'modelClassName' => $param->getModelName(),
        ];
        $content = file_get_contents($templateFile);
        $content = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($varMaps) {
            return $varMaps[$matches[1]]??"模板未定义";
        }, $content);
        return $content;
    }


    protected function configure()
    {
        $this->setName('auto_code')
            ->addArgument('module', Argument::OPTIONAL, "模块名")
            ->addOption('model', null, Option::VALUE_REQUIRED, '模型的名字,不传用数据表名')
            ->setDescription('代码生成器');
    }

    protected function execute(Input $input, Output $output)
    {
        $param = $this->parseTableSql();
        $module = trim($input->getArgument('module'));
        if (!$module) {
            exception("请输入模块名,以便确定生成文件的存放目录");
        }
        if ($input->hasOption('model')) {
            $param->inputTableName = $input->getOption('model');
        }

        $param->module = $module;
        $dto = AutoCode::createDTO($param);
        $model = AutoCode::createModel($param);
        $validate = AutoCode::createValidate($param);
        $controller = AutoCode::createController($param);
        $service = AutoCode::createService($param);

        !is_file($param->getDtoFilePath()) ? file_put_contents($param->getDtoFilePath(), $dto) : $output->writeln("DTO文件已存在,没有重新生成");
        !is_file($param->getModeFilePath()) ? file_put_contents($param->getModeFilePath(), $model) : $output->writeln("Model文件已存在,没有重新生成");
        !is_file($param->getValidateFilePath()) ? file_put_contents($param->getValidateFilePath(), $validate) : $output->writeln("Validate文件已存在,没有重新生成");
        !is_file($param->getControllerFilePath()) ? file_put_contents($param->getControllerFilePath(), $controller) : $output->writeln("Controller文件已存在,没有重新生成");
        !is_file($param->getServiceFilePath()) ? file_put_contents($param->getServiceFilePath(), $service) : $output->writeln("Service文件已存在,没有重新生成");

        $output->writeln("基本代码已经自动生成,祝你工作愉快!");
    }
}
  1. AutoCode所使用的配置类:AutoCodeParam
serviceName ?: Loader::parseName($this->getInputTableName(), 1) . "Service";
    }


    public function getModelName()
    {
        return $this->modelName ?: Loader::parseName($this->getInputTableName(), 1) . "Model";
    }

    public function getInputTableName()
    {
        return $this->inputTableName ?: $this->tableName;
    }

    public function getControllerName()
    {
        return $this->controllerName ?: Loader::parseName($this->getInputTableName(), 1);
    }

    public function getValidateName()
    {
        return $this->validateName ?: Loader::parseName($this->getInputTableName(), 1) . "Validate";
    }

    public function getDtoName()
    {
        return $this->validateName ?: Loader::parseName($this->getInputTableName(), 1) . "Param";
    }

    public function getNameSpace()
    {
        return $this->nameSpace . "\\" . $this->module;
    }

    public function getBaseDirPath()
    {
        // APP_PATH
        return APP_PATH . $this->module . "/";
    }

    public function getModeFilePath()
    {
        return $this->getBaseDirPath() . "model/" . $this->getModelName() . ".php";
    }

    public function getValidateFilePath()
    {
        return $this->getBaseDirPath() . "validate/" . $this->getValidateName() . ".php";
    }

    public function getControllerFilePath()
    {
        return $this->getBaseDirPath() . "controller/admin/" . $this->getControllerName() . ".php";
    }

    public function getServiceFilePath()
    {
        return $this->getBaseDirPath() . "service/" . $this->getServiceName() . ".php";
    }

    public function getDtoFilePath()
    {
        return $this->getBaseDirPath() . "logic/" . $this->getDtoName() . ".php";
    }
}
  1. 对应的controller、model、service、dto、validate模板文件

控制器的

param();
        $result = {{serviceClassName}}::getInstance()->lists(TpQuerySet::create(
            ['queryParam' => $param]
        ));
        return $this->json($result);
    }

    /**
     * {{tableDesc}}详情
     * @param Request $request
     * @return \think\Response\Json
     */
    public function detail(Request $request)
    {
        $param = $request->param();
        $result = {{serviceClassName}}::getInstance()->setSearchPrefix('')
            ->detail(TpQuerySet::create(
                ['queryParam' => $param]
            ));
        return $this->json($result);
    }


    /**
     * 创建{{tableDesc}}
     * @param Request $request
     * @return \think\Response\Json
     */
    public function create(Request $request)
    {
        {{validateClassName}}::getInstance()->goCheck();
        $result = {{serviceClassName}}::getInstance()->create(
            {{dtoClassName}}::create($request->param())
        );
        return $this->json(['id' => $result]);
    }

    /**
     * 编辑{{tableDesc}}
     * @param Request $request
     * @return \think\Response\Json
     */
    public function update(Request $request)
    {
        {{validateClassName}}::getInstance()->goCheck();
        $result = {{serviceClassName}}::getInstance()->update(
            {{dtoClassName}}::create($request->param())
        );
        return $this->json(['id' => $result]);
    }

    /**
     * 删除{{tableDesc}}
     * @param Request $request
     * @return \think\Response\Json
     */
    public function delete(Request $request)
    {
        {{serviceClassName}}::getInstance()->deleteIds();
        return $this->json(['msg' => '删除成功']);
    }
}

dto

model

service

getQueryParam();
        // 过滤搜索条件
        $param = $this->filterSearchParam($param);
        $condition = [];
        foreach ($param as $key => $value) {
            $key = $cyQuery->getQueryKeyByField($key);
            $condition[] = TpQuerySet::buildCond($key, $value);
        }
        $cyQuery->setWhere($condition);
        return $cyQuery;
    }

    /**
     * 数据分页列表页
     * @param TpQuerySet $querySet
     * @return \think\Paginator
     */
    public function lists(TpQuerySet $querySet)
    {
        $list = $this->search($querySet)->paginate(TpQuerySet::pageSize());
        return $list;
    }

    /**
     * 搜索查找
     * @param TpQuerySet $cyQuerySet
     * @return Query
     */
    public function search(TpQuerySet $cyQuerySet)
    {
        $model = {{modelClassName}}::getInstance();
        $cyQuerySet->setModel($model);
        $querySet = $this->buildQuerySet($cyQuerySet);
        $query = $model->queryWithSet($querySet);
        return $query;
    }

    /**
     * 详情页
     * @param TpQuerySet $querySet
     * @return array|null|\PDOStatement|string|\think\Model
     */
    public function detail(TpQuerySet $querySet)
    {
        $result = $this->search($querySet)->find();
        return $result;
    }

    /**
     * 排序
     *
     * @param $sorts
     * @return array
     */
    public function sort($sorts)
    {
        foreach ($sorts as $id => $sort) {
            $res = {{modelClassName}}::update(['sort' => $sort], $id);
            if (!$res) {
                exception($res->getError(), 100100);
            }
        }
        return ['msg' => '操作成功'];
    }

    /**
     * @param {{dtoClassName}} $param
     * @param int $returnType
     * @return mixed
     * @throws \Exception
     */
    public function create({{dtoClassName}} $param, $returnType = 0)
    {
        Db::startTrans();
        try {
            $model = {{modelClassName}}::create($param->toArr());
            Db::commit();
            return $returnType == 0 ? $model->getKey() : $model;
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 更新
     * @param {{dtoClassName}} $param
     * @param array $where
     * @return bool
     * @throws \Exception
     */
    public function update({{dtoClassName}} $param, $where = [])
    {
        Db::startTrans();
        try {
            if (!$where) {
                $where = [$this->getPk() => $param->id];
            }
            $model = {{modelClassName}}::update($param->toArr(), $where);
            Db::commit();
            return $model->getKey();
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

}

validate

 [''],
        'update' => ['{{pk}}']
    ];
}

功能展示

假设要做一个和省市区域相关的功能,把数据表的创建语句放在一个文件如下。

基于TP实现的基本业务代码生成器_第1张图片

执行命令 在system模块下生成文件。

基于TP实现的基本业务代码生成器_第2张图片
image.png

文件已经生成。

基于TP实现的基本业务代码生成器_第3张图片
image.png

查询实验:直接可以根据任何字段搜索数据,添加和编辑删除同样可以实现。


基于TP实现的基本业务代码生成器_第4张图片
image.png

大大的解放了双手,基本代码完全自动生成,对于其他的功能,只要定义Model和Model的关联关系,在service完善就可以了。人生苦短,不能总搬砖。

你可能感兴趣的:(基于TP实现的基本业务代码生成器)