是否厌倦了基本业务的增删改查,仔细想想,每次业务基本都是一样的。都是根据一个数据表来生成对应的控制器、模型、DTO、service、验证器。所以自己对日常的代码进行了高度抽象,通过正则表达式,自动生成了对应的代码文件及其注释。本功能结合tp的command类实现。
代码实现:
- 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("基本代码已经自动生成,祝你工作愉快!");
}
}
- 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";
}
}
- 对应的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}}']
];
}
功能展示
假设要做一个和省市区域相关的功能,把数据表的创建语句放在一个文件如下。
执行命令 在system模块下生成文件。
文件已经生成。
查询实验:直接可以根据任何字段搜索数据,添加和编辑删除同样可以实现。
大大的解放了双手,基本代码完全自动生成,对于其他的功能,只要定义Model和Model的关联关系,在service完善就可以了。人生苦短,不能总搬砖。