diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php
index 619d535d..69459497 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("Build Successed");
}
+ 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
@@ -386,6 +551,23 @@ class Crud extends Command
* @return mixed
*/
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 cfb67d14..530b8629 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 00000000..d10c040f
--- /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 05d2e6f0..8e1b495d 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 00000000..39b3c901
--- /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 00000000..e0a8c090
--- /dev/null
+++ b/application/admin/command/Crud/stubs/relationmodel.stub
@@ -0,0 +1,12 @@
+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('/(? $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 fa9c086b..b587b255 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 f28991ac..48a38e5f 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 = ' ' + __(value) + '';
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('' + __(value) + '');
});
return html.join(' ');
diff --git a/public/assets/js/require-table.js b/public/assets/js/require-table.js
index 88b86ead..dc64e6bf 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 = ' ' + __(value) + '';
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('' + __(value) + '');
});
return html.join(' ');