作者 Karson

新增一键生成关联表查询列表

新增基类控制器的searchFields和relationSearch属性
修复status和flag当为空值时的BUG
... ... @@ -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';
... ...
... ... @@ -22,4 +22,5 @@ class {%controllerName%} extends Backend
parent::_initialize();
$this->model = model('{%modelName%}');
}
{%controllerIndex%}
}
... ...
/**
* 查看
*/
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
... ...
... ... @@ -6,11 +6,15 @@ use think\Model;
class {%modelName%} extends Model
{
// 表名,不含前缀
protected $name = '{%modelTableName%}';
// 自动写入时间戳字段
protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%};
// 定义时间戳字段名
protected $createTime = {%createTime%};
protected $updateTime = {%updateTime%};
{%modelMethod%}
}
... ...
public function {%relationMethod%}()
{
return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%pk%}')->setEagerlyType(0);
}
\ No newline at end of file
... ...
<?php
namespace {%modelNamespace%};
use think\Model;
class {%relationModelName%} extends Model
{
// 表名,不含前缀
protected $name = '{%relationModelTableName%}';
}
... ...
... ... @@ -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)
... ...
... ... @@ -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++) {
... ...
... ... @@ -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(' ');
... ...
... ... @@ -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(' ');
... ...