是否厭倦了基本業(yè)務的增刪改查,仔細想想,每次業(yè)務基本都是一樣的。都是根據(jù)一個數(shù)據(jù)表來生成對應的控制器、模型、DTO、service、驗證器。所以自己對日常的代碼進行了高度抽象,通過正則表達式,自動生成了對應的代碼文件及其注釋。本功能結(jié)合tp的command類實現(xiàn)。
代碼實現(xiàn):
- 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)自動生成,祝你工作愉快!");
}
}
- 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";
}
}
- 對應的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完善就可以了。人生苦短,不能總搬磚。