基于TP實現(xiàn)的基本業(yè)務代碼生成器

是否厭倦了基本業(yè)務的增刪改查,仔細想想,每次業(yè)務基本都是一樣的。都是根據(jù)一個數(shù)據(jù)表來生成對應的控制器、模型、DTO、service、驗證器。所以自己對日常的代碼進行了高度抽象,通過正則表達式,自動生成了對應的代碼文件及其注釋。本功能結(jié)合tp的command類實現(xiàn)。

代碼實現(xiàn):

  1. AutoCode 類
<?php

namespace app\common\command\code;

/**
 * Created by PhpStorm.
 * User: guodong
 * Date: 2019/7/23
 * Time: 下午1:35
 */
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;


class AutoCode extends Command
{
    public function parseTableSql(): AutoCodeParam
    {
        $file = ROOT_PATH . 'auto_code.sql';
        if (!is_file($file)) {
            exception("文件不存在");
        }
        $tableSQL = file_get_contents(ROOT_PATH . 'auto_code.sql');
        if (empty($tableSQL)) {
            exception("SQL不存在無法創(chuàng)建");
        }
        $this->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創(chuàng)建表有些字段缺少注釋,不能生成");
        }
        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 = "參數(shù)名稱 | 參數(shù)類型 | 是否必填 | 說明
:--- | :---: | :---: | :---
";
        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;
    }


    /**
     * 生成服務器返回結(jié)果文檔
     * @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 = "參數(shù)名稱 | 參數(shù)類型  | 說明
:--- | :---: | :---
";
        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 ";
            //數(shù)據(jù)類型
            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;
    }


    /**
     * 創(chuàng)建模型
     * @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;
    }


    /**
     * 創(chuàng)建驗證器
     * @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;
    }

    /**
     * 創(chuàng)建控制器
     * @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;
    }


    /**
     * 創(chuàng)建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, '模型的名字,不傳用數(shù)據(jù)表名')
            ->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("基本代碼已經(jīng)自動生成,祝你工作愉快!");
    }
}
  1. AutoCode所使用的配置類:AutoCodeParam
<?php
/**
 * Created by PhpStorm.
 * User: guodong
 * Date: 2019/7/23
 * Time: 下午3:57
 */

namespace app\common\command\code;

use app\common\lib\DTO;
use think\Loader;


/**
 * 代碼生成器參數(shù)類
 * Class AutoCodeParam
 * @package app\common\command
 */
class AutoCodeParam extends DTO
{

    /**
     * 控制器名字
     * @var string
     */
    public $controllerName = '';

    /**
     * 模型名
     * @var string
     */
    public $modelName = '';

    /**
     * service名字
     * @var string
     */
    public $serviceName = '';

    /**
     * dto的名字
     * @var string
     */
    public $dtoName = '';

    /**
     * 驗證器的名字
     * @var string
     */
    public $validateName = '';

    /**
     * 數(shù)據(jù)表的主鍵id
     * @var string
     */
    public $pkName = 'id';

    /**
     * 原始的sql
     * @var string
     */
    public $tableSQL = "";

    /**
     * 表名
     * @var string
     */
    public $tableName = "";

    /**
     * 手動輸入用來創(chuàng)建類名用的
     * @var string
     */
    public $inputTableName = "";

    /**
     * 命名空間
     * @var string
     */
    protected $nameSpace = "app";

    /**
     * 模塊名
     * @var string
     */
    public $module = "";

    /**
     * 數(shù)據(jù)表的描述
     * @var string
     */
    public $tableDesc = "";


    public function getServiceName()
    {
        return $this->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模板文件

控制器的

<?php
/**
 * Created by PhpStorm.
 * User: SmartCodeTool
 * Date: {{date}}
 * Time: {{time}}
 */

namespace {{nameSpace}};

use app\common\lib\AdminController;
use app\common\lib\TpQuerySet;
use {{validateNameSpace}};
use {{serviceNameSpace}};
use {{dtoNameSpace}};
use think\Request;

/**
 * {{tableName}}
 * Class {{className}}
 * @package {{nameSpace}}
 */
class {{className}} extends AdminController
{
    /**
     * {{tableDesc}}列表
     * @param Request $request
     * @return \think\Response\Json
     */
    public function lists(Request $request)
    {
        $param = $request->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);
    }


    /**
     * 創(chuàng)建{{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

<?php
/**
 * Created by PhpStorm.
 * User: SmartCodeTool
 * Date: {{date}}
 * Time: {{time}}
 */

namespace {{nameSpace}};

use app\common\lib\DTO;

class {{className}} extends DTO
{
   {{classBody}}
}

model

<?php
/**
 * Created by PhpStorm.
 * User: SmartCodeTool
 * Date: {{date}}
 * Time: {{time}}
 */


namespace {{nameSpace}};

use app\common\lib\BaseModel;

class {{className}} extends BaseModel
{
    protected $table = "{{tableName}}";

    protected $pk = "{{pk}}";
}

service

<?php
/**
 * Created by PhpStorm.
 * User: SmartCodeTool
 * Date: {{date}}
 * Time: {{time}}
 */
namespace {{nameSpace}};

use app\common\lib\BaseService;
use app\common\lib\TpQuerySet;
use {{modelNameSpace}};
use {{dtoNameSpace}};
use think\Db;
use think\db\Query;

/**
 * {{tableDesc}} service
 * Class {{className}}
 * @package app\dbase\service
 */
class {{className}} extends BaseService
{
    /**
     * 搜索條件的前綴
     *
     * @var string
     */
    protected $searchPrefix = "search_";

    /**
     * 根據(jù)外面?zhèn)鬟f進來的參數(shù)構(gòu)造查詢對象,拼接where
     * @param TpQuerySet $cyQuery
     * @return TpQuerySet
     */
    public function buildQuerySet(TpQuerySet $cyQuery)
    {
        // 獲取傳入?yún)?shù)
        $param = $cyQuery->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;
    }

    /**
     * 數(shù)據(jù)分頁列表頁
     * @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

<?php
/**
 * Created by PhpStorm.
 * User: guodong
 * Date: {{date}}
 * Time: {{time}}
 */

namespace {{nameSpace}};;

use app\common\lib\BaseValidate;

class {{className}} extends BaseValidate
{
    /**
     * 驗證規(guī)則
     * @var array
     */
    protected $rule = [
{{rules}}
    ];

    protected $scene = [
        'create' => [''],
        'update' => ['{{pk}}']
    ];
}

功能展示

假設要做一個和省市區(qū)域相關的功能,把數(shù)據(jù)表的創(chuàng)建語句放在一個文件如下。

執(zhí)行命令 在system模塊下生成文件。

image.png

文件已經(jīng)生成。

image.png

查詢實驗:直接可以根據(jù)任何字段搜索數(shù)據(jù),添加和編輯刪除同樣可以實現(xiàn)。


image.png

大大的解放了雙手,基本代碼完全自動生成,對于其他的功能,只要定義Model和Model的關聯(lián)關系,在service完善就可以了。人生苦短,不能總搬磚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容