From 8da2b007bd1046f17cadbacfaf702437cb0423eb Mon Sep 17 00:00:00 2001 From: Karson <karsonzhang@163.com> Date: Tue, 30 May 2017 19:27:19 +0800 Subject: [PATCH] 新增一键生成关联表查询列表 新增基类控制器的searchFields和relationSearch属性 修复status和flag当为空值时的BUG --- application/admin/command/Crud.php | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- application/admin/command/Crud/stubs/controller.stub | 1 + application/admin/command/Crud/stubs/controllerindex.stub | 30 ++++++++++++++++++++++++++++++ application/admin/command/Crud/stubs/model.stub | 6 +++++- application/admin/command/Crud/stubs/modelmethod.stub | 5 +++++ application/admin/command/Crud/stubs/relationmodel.stub | 12 ++++++++++++ application/common/controller/Backend.php | 42 ++++++++++++++++++++++++++++-------------- public/assets/js/backend.js | 2 +- public/assets/js/require-backend.min.js | 6 +++--- public/assets/js/require-table.js | 4 ++-- 10 files changed, 294 insertions(+), 50 deletions(-) create mode 100644 application/admin/command/Crud/stubs/controllerindex.stub create mode 100644 application/admin/command/Crud/stubs/modelmethod.stub create mode 100644 application/admin/command/Crud/stubs/relationmodel.stub diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php index 619d535..6945949 100644 --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -24,6 +24,11 @@ class Crud extends Command ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) + ->addOption('relation', 'r', Option::VALUE_OPTIONAL, 'relation table name without prefix', null) + ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL, 'relation model name', null) + ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL, 'relation foreign key', null) + ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL, 'relation primary key', null) + ->addOption('mode', 'o', Option::VALUE_OPTIONAL, 'relation table mode,hasone or belongsto', 'hasone') ->setDescription('Build CRUD controller and model from table'); } @@ -44,8 +49,26 @@ class Crud extends Command { throw new Exception('table name can\'t empty'); } + //关联表 + $relation = $input->getOption('relation'); + //自定义关联表模型 + $relationModel = $input->getOption('relationmodel'); + //模式 + $mode = $input->getOption('mode'); + //外键 + $relationForeignKey = $input->getOption('relationforeignkey'); + //主键 + $relationPrimaryKey = $input->getOption('relationprimarykey'); + //如果有启用关联模式 + if ($relation && !in_array($mode, ['hasone', 'belongsto'])) + { + throw new Exception("relation table only work in hasone or belongsto mode"); + } + $dbname = Config::get('database.database'); $prefix = Config::get('database.prefix'); + + //检查主表 $tableName = $prefix . $table; $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); if (!$tableInfo) @@ -54,6 +77,17 @@ class Crud extends Command } $tableInfo = $tableInfo[0]; + //检查关联表 + if ($relation) + { + $relationTableName = $prefix . $relation; + $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); + if (!$relationTableInfo) + { + throw new Exception("relation table not found"); + } + } + //根据表名匹配对应的Fontawesome图标 $iconPath = ROOT_PATH . str_replace('/', DS, '/public/assets/libs/font-awesome/less/variables.less'); $iconName = is_file($iconPath) && stripos(file_get_contents($iconPath), '@fa-var-' . $table . ':') ? $table : 'fa fa-circle-o'; @@ -72,20 +106,13 @@ class Crud extends Command } //模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入model,不支持目录层级 - if (!$model) - { - $modelarr = explode('_', strtolower($table)); - foreach ($modelarr as $k => &$v) - $v = ucfirst($v); - unset($v); - $modelName = implode('', $modelarr); - } - else - { - $modelName = ucfirst($model); - } + $modelName = $this->getModelName($model, $table); $modelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $modelName . '.php'; + //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 + $relationModelName = $this->getModelName($relationModel, $relation); + $relationModelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationModelName . '.php'; + //非覆盖模式时如果存在模型文件则报错 if (is_file($modelFile) && !$force) { @@ -95,12 +122,26 @@ class Crud extends Command require $adminPath . 'common.php'; //从数据库中获取表字段信息 - $columnList = Db::query("SELECT * FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND table_name = ? ORDER BY ORDINAL_POSITION", [$dbname, $tableName]); + $sql = "SELECT * FROM `information_schema`.`columns` " + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + $columnList = Db::query($sql, [$dbname, $tableName]); + $relationColumnList = []; + if ($relation) + { + $relationColumnList = Db::query($sql, [$dbname, $relationTableName]); + } - $fields = []; + $fieldArr = []; foreach ($columnList as $k => $v) { - $fields[] = $v['COLUMN_NAME']; + $fieldArr[] = $v['COLUMN_NAME']; + } + + $relationFieldArr = []; + foreach ($relationColumnList as $k => $v) + { + $relationFieldArr[] = $v['COLUMN_NAME']; } $addList = []; @@ -110,20 +151,68 @@ class Crud extends Command $field = 'id'; $order = 'id'; $priDefined = FALSE; - $prikey = ''; + $priKey = ''; + $relationPriKey = ''; foreach ($columnList as $k => $v) { if ($v['COLUMN_KEY'] == 'PRI') { - $prikey = $v['COLUMN_NAME']; + $priKey = $v['COLUMN_NAME']; break; } } - if (!$prikey) + if (!$priKey) { throw new Exception('Primary key not found!'); } - $order = $prikey; + if ($relation) + { + foreach ($relationColumnList as $k => $v) + { + if ($v['COLUMN_KEY'] == 'PRI') + { + $relationPriKey = $v['COLUMN_NAME']; + break; + } + } + if (!$relationPriKey) + { + throw new Exception('Relation Primary key not found!'); + } + } + $order = $priKey; + + + //如果是关联模型 + if ($relation) + { + if ($mode == 'hasone') + { + $relationForeignKey = $relationForeignKey ? $relationForeignKey : $table . "_id"; + $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $priKey; + if (!in_array($relationForeignKey, $relationFieldArr)) + { + throw new Exception('relation table must be contain field:' . $relationForeignKey); + } + if (!in_array($relationPrimaryKey, $fieldArr)) + { + throw new Exception('table must be contain field:' . $relationPrimaryKey); + } + } + else + { + $relationForeignKey = $relationForeignKey ? $relationForeignKey : $relation . "_id"; + $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $relationPriKey; + if (!in_array($relationForeignKey, $fieldArr)) + { + throw new Exception('table must be contain field:' . $relationForeignKey); + } + if (!in_array($relationPrimaryKey, $relationFieldArr)) + { + throw new Exception('relation table must be contain field:' . $relationPrimaryKey); + } + } + } try { @@ -309,6 +398,30 @@ class Crud extends Command $order = $field == 'weigh' ? 'weigh' : $order; } } + + $relationPriKey = 'id'; + $relationFieldArr = []; + foreach ($relationColumnList as $k => $v) + { + $relationField = $v['COLUMN_NAME']; + $relationFieldArr[] = $field; + + $relationField = strtolower($relationModelName) . "." . $relationField; + // 语言列表 + if ($v['COLUMN_COMMENT'] != '') + { + $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); + } + + //过滤text类型字段 + if ($v['DATA_TYPE'] != 'text') + { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($relationField); + } + } + + //JS最后一列加上操作列 $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}"; $addList = implode("\n", array_filter($addList)); @@ -333,6 +446,7 @@ class Crud extends Command $controllerNamespace = "{$appNamespace}\\{$moduleName}\\controller" . ($controllerDir ? "\\" : "") . str_replace('/', "\\", $controllerDir); $modelNamespace = "{$appNamespace}\\" . ($local ? $moduleName : "common") . "\\model"; + $data = [ 'controllerNamespace' => $controllerNamespace, 'modelNamespace' => $modelNamespace, @@ -342,7 +456,7 @@ class Crud extends Command 'modelName' => $modelName, 'tableComment' => $tableComment, 'iconName' => $iconName, - 'pk' => $prikey, + 'pk' => $priKey, 'order' => $order, 'table' => $table, 'tableName' => $tableName, @@ -350,15 +464,49 @@ class Crud extends Command 'editList' => $editList, 'javascriptList' => $javascriptList, 'langList' => $langList, - 'modelAutoWriteTimestamp' => in_array('createtime', $fields) || in_array('updatetime', $fields) ? "'int'" : 'false', - 'createTime' => in_array('createtime', $fields) ? "'createtime'" : 'false', - 'updateTime' => in_array('updatetime', $fields) ? "'updatetime'" : 'false', + 'modelAutoWriteTimestamp' => in_array('createtime', $fieldArr) || in_array('updatetime', $fieldArr) ? "'int'" : 'false', + 'createTime' => in_array('createtime', $fieldArr) ? "'createtime'" : 'false', + 'updateTime' => in_array('updatetime', $fieldArr) ? "'updatetime'" : 'false', + 'modelTableName' => $table, + 'relationModelTableName' => $relation, + 'relationModelName' => $relationModelName, + 'relationWith' => '', + 'relationMethod' => '', + 'relationModel' => '', + 'relationForeignKey' => '', + 'relationPrimaryKey' => '', + 'relationSearch' => $relation ? 'true' : 'false', + 'controllerIndex' => '', + 'modelMethod' => '', ]; + //如果使用关联模型 + if ($relation) + { + //需要构造关联的方法 + $data['relationMethod'] = strtolower($relationModelName); + //预载入的方法 + $data['relationWith'] = "->with('{$data['relationMethod']}')"; + //需要重写index方法 + $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); + //关联的模式 + $data['relationMode'] = $mode == 'hasone' ? 'hasOne' : 'belongsTo'; + //关联字段 + $data['relationForeignKey'] = $relationForeignKey; + $data['relationPrimaryKey'] = $relationPrimaryKey ? $relationPrimaryKey : $priKey; + //构造关联模型的方法 + $data['modelMethod'] = $this->getReplacedStub('modelmethod', $data); + } + // 生成控制器文件 $result = $this->writeToFile('controller', $data, $controllerFile); // 生成模型文件 $result = $this->writeToFile('model', $data, $modelFile); + if ($relation && !is_file($relationModelFile)) + { + // 生成关联模型文件 + $result = $this->writeToFile('relationmodel', $data, $relationModelFile); + } // 生成视图文件 $result = $this->writeToFile('add', $data, $addFile); $result = $this->writeToFile('edit', $data, $editFile); @@ -378,6 +526,23 @@ class Crud extends Command $output->writeln("<info>Build Successed</info>"); } + protected function getModelName($model, $table) + { + if (!$model) + { + $modelarr = explode('_', strtolower($table)); + foreach ($modelarr as $k => &$v) + $v = ucfirst($v); + unset($v); + $modelName = implode('', $modelarr); + } + else + { + $modelName = ucfirst($model); + } + return $modelName; + } + /** * 写入到文件 * @param string $name @@ -387,6 +552,23 @@ class Crud extends Command */ protected function writeToFile($name, $data, $pathname) { + $content = $this->getReplacedStub($name, $data); + + if (!is_dir(dirname($pathname))) + { + mkdir(strtolower(dirname($pathname)), 0755, true); + } + return file_put_contents($pathname, $content); + } + + /** + * 获取替换后的数据 + * @param string $name + * @param array $data + * @return string + */ + protected function getReplacedStub($name, $data) + { $search = $replace = []; foreach ($data as $k => $v) { @@ -395,12 +577,7 @@ class Crud extends Command } $stub = file_get_contents($this->getStub($name)); $content = str_replace($search, $replace, $stub); - - if (!is_dir(dirname($pathname))) - { - mkdir(strtolower(dirname($pathname)), 0755, true); - } - return file_put_contents($pathname, $content); + return $content; } /** @@ -570,6 +747,7 @@ EOD; { $lang = ucfirst($field); $html = str_repeat(" ", 24) . "{field: '{$field}', title: __('{$lang}')"; + $field = substr($field, stripos($field, '.') + 1); $formatter = ''; if ($field == 'status') $formatter = 'status'; diff --git a/application/admin/command/Crud/stubs/controller.stub b/application/admin/command/Crud/stubs/controller.stub index cfb67d1..530b862 100644 --- a/application/admin/command/Crud/stubs/controller.stub +++ b/application/admin/command/Crud/stubs/controller.stub @@ -22,4 +22,5 @@ class {%controllerName%} extends Backend parent::_initialize(); $this->model = model('{%modelName%}'); } +{%controllerIndex%} } diff --git a/application/admin/command/Crud/stubs/controllerindex.stub b/application/admin/command/Crud/stubs/controllerindex.stub new file mode 100644 index 0000000..d10c040 --- /dev/null +++ b/application/admin/command/Crud/stubs/controllerindex.stub @@ -0,0 +1,30 @@ + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = {%relationSearch%}; + + if ($this->request->isAjax()) + { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + {%relationWith%} + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + {%relationWith%} + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/model.stub b/application/admin/command/Crud/stubs/model.stub index 05d2e6f..8e1b495 100644 --- a/application/admin/command/Crud/stubs/model.stub +++ b/application/admin/command/Crud/stubs/model.stub @@ -6,11 +6,15 @@ use think\Model; class {%modelName%} extends Model { - + // 表名,不含前缀 + protected $name = '{%modelTableName%}'; + // 自动写入时间戳字段 protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%}; // 定义时间戳字段名 protected $createTime = {%createTime%}; protected $updateTime = {%updateTime%}; + +{%modelMethod%} } diff --git a/application/admin/command/Crud/stubs/modelmethod.stub b/application/admin/command/Crud/stubs/modelmethod.stub new file mode 100644 index 0000000..39b3c90 --- /dev/null +++ b/application/admin/command/Crud/stubs/modelmethod.stub @@ -0,0 +1,5 @@ + + public function {%relationMethod%}() + { + return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%pk%}')->setEagerlyType(0); + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/relationmodel.stub b/application/admin/command/Crud/stubs/relationmodel.stub new file mode 100644 index 0000000..e0a8c09 --- /dev/null +++ b/application/admin/command/Crud/stubs/relationmodel.stub @@ -0,0 +1,12 @@ +<?php + +namespace {%modelNamespace%}; + +use think\Model; + +class {%relationModelName%} extends Model +{ + // 表名,不含前缀 + protected $name = '{%relationModelTableName%}'; + +} diff --git a/application/common/controller/Backend.php b/application/common/controller/Backend.php index afbd2bf..91f658a 100644 --- a/application/common/controller/Backend.php +++ b/application/common/controller/Backend.php @@ -60,6 +60,16 @@ class Backend extends Controller protected $auth = null; /** + * 快速搜索时执行查找的字段 + */ + protected $searchFields = 'id'; + + /** + * 是否是关联查询 + */ + protected $relationSearch = false; + + /** * 引入后台控制器的traits */ use \app\admin\library\traits\Backend; @@ -154,11 +164,13 @@ class Backend extends Controller /** * 生成查询所需要的条件,排序方式 * @param mixed $searchfields 查询条件 + * @param boolean $relationSearch 是否关联查询 * @return array */ - protected function buildparams($searchfields = NULL) + protected function buildparams($searchfields = null, $relationSearch = null) { - $searchfields = is_null($searchfields) ? 'id' : $searchfields; + $searchfields = is_null($searchfields) ? $this->searchFields : $searchfields; + $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch; $search = $this->request->get("search", ''); $filter = $this->request->get("filter", ''); $op = $this->request->get("op", ''); @@ -180,25 +192,27 @@ class Backend extends Controller } $where[] = "(" . implode(' OR ', $searchlist) . ")"; } - $table = ''; - if (!empty($this->model)) + $modelName = ''; + if ($relationSearch) { - $class = get_class($this->model); - $name = basename(str_replace('\\', '/', $class)); - $table = $this->model->db(false)->getTable($name); - $table = $table . "."; - } - if (stripos($sort, ".") === false) - { - - $sort = $table . $sort; + if (!empty($this->model)) + { + $class = get_class($this->model); + $name = basename(str_replace('\\', '/', $class)); + $name = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $name)); + $modelName = $name . "."; + } + if (stripos($sort, ".") === false) + { + $sort = $modelName . $sort; + } } foreach ($filter as $k => $v) { $sym = isset($op[$k]) ? $op[$k] : '='; if (stripos($k, ".") === false) { - $k = $table . $k; + $k = $modelName . $k; } $sym = isset($op[$k]) ? $op[$k] : $sym; switch ($sym) diff --git a/public/assets/js/backend.js b/public/assets/js/backend.js index fa9c086..b587b25 100755 --- a/public/assets/js/backend.js +++ b/public/assets/js/backend.js @@ -305,7 +305,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'moment'], function ($ if (typeof Lang[string] == 'object') return Lang[string]; string = Lang[string]; - } else if (string.indexOf('.') !== -1) { + } else if (string.indexOf('.') !== -1 && false) { var arr = string.split('.'); var current = Lang[arr[0]]; for (var i = 1; i < arr.length; i++) { diff --git a/public/assets/js/require-backend.min.js b/public/assets/js/require-backend.min.js index f28991a..48a38e5 100644 --- a/public/assets/js/require-backend.min.js +++ b/public/assets/js/require-backend.min.js @@ -6722,7 +6722,7 @@ define('backend',['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'moment'], f if (typeof Lang[string] == 'object') return Lang[string]; string = Lang[string]; - } else if (string.indexOf('.') !== -1) { + } else if (string.indexOf('.') !== -1 && false) { var arr = string.split('.'); var current = Lang[arr[0]]; for (var i = 1; i < arr.length; i++) { @@ -7863,7 +7863,7 @@ define('table',['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap } value = value.toString(); var color = value && typeof colorArr[value] !== 'undefined' ? colorArr[value] : 'primary'; - value = value[0].toUpperCase() + value.substr(1); + value = value.charAt(0).toUpperCase() + value.slice(1); //渲染状态 var html = '<span class="text-' + color + '"><i class="fa fa-circle"></i> ' + __(value) + '</span>'; return html; @@ -7891,7 +7891,7 @@ define('table',['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap if (value == '') return true; var color = value && typeof colorArr[value] !== 'undefined' ? colorArr[value] : 'primary'; - value = value[0].toUpperCase() + value.substr(1); + value = value.charAt(0).toUpperCase() + value.slice(1); html.push('<span class="label label-' + color + '">' + __(value) + '</span>'); }); return html.join(' '); diff --git a/public/assets/js/require-table.js b/public/assets/js/require-table.js index 88b86ea..dc64e6b 100644 --- a/public/assets/js/require-table.js +++ b/public/assets/js/require-table.js @@ -283,7 +283,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table', } value = value.toString(); var color = value && typeof colorArr[value] !== 'undefined' ? colorArr[value] : 'primary'; - value = value[0].toUpperCase() + value.substr(1); + value = value.charAt(0).toUpperCase() + value.slice(1); //渲染状态 var html = '<span class="text-' + color + '"><i class="fa fa-circle"></i> ' + __(value) + '</span>'; return html; @@ -311,7 +311,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table', if (value == '') return true; var color = value && typeof colorArr[value] !== 'undefined' ? colorArr[value] : 'primary'; - value = value[0].toUpperCase() + value.substr(1); + value = value.charAt(0).toUpperCase() + value.slice(1); html.push('<span class="label label-' + color + '">' + __(value) + '</span>'); }); return html.join(' '); -- libgit2 0.24.0