diff --git a/README.md b/README.md index c28f5b4b..9025d314 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。 * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置 * 支持单管理员多角色 * 支持目录和控制器结构一键生成权限节点 + * 支持管理子级数据或个人数据 * 完善的前端功能组件开发 * 基于`AdminLTE`二次开发 * 基于`Bootstrap`开发,自适应手机、平板、PC diff --git a/application/admin/command/Api.php b/application/admin/command/Api.php old mode 100644 new mode 100755 index 28885148..57cdfff1 --- a/application/admin/command/Api.php +++ b/application/admin/command/Api.php @@ -17,17 +17,17 @@ class Api extends Command { $site = Config::get('site'); $this - ->setName('api') - ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') - ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') - ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') - ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') - ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) - ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) - ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) - ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) - ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') - ->setDescription('Compress js and css file'); + ->setName('api') + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) + ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') + ->setDescription('Compress js and css file'); } protected function execute(Input $input, Output $output) @@ -37,24 +37,22 @@ class Api extends Command $force = $input->getOption('force'); $url = $input->getOption('url'); $language = $input->getOption('language'); + $language = $language ? $language : 'zh-cn'; $langFile = $apiDir . 'lang' . DS . $language . '.php'; - if (!is_file($langFile)) - { + if (!is_file($langFile)) { throw new Exception('language file not found'); } - $lang = include $langFile; + $lang = include_once $langFile; // 目标目录 $output_dir = ROOT_PATH . 'public' . DS; $output_file = $output_dir . $input->getOption('output'); - if (is_file($output_file) && !$force) - { + if (is_file($output_file) && !$force) { throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); } // 模板文件 $template_dir = $apiDir . 'template' . DS; $template_file = $template_dir . $input->getOption('template'); - if (!is_file($template_file)) - { + if (!is_file($template_file)) { throw new Exception('template file not found'); } // 额外的类 @@ -67,19 +65,30 @@ class Api extends Command $module = $input->getOption('module'); $moduleDir = APP_PATH . $module . DS; - if (!is_dir($moduleDir)) - { + if (!is_dir($moduleDir)) { throw new Exception('module not found'); } + + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + if (extension_loaded('opcache')) { + $configuration = opcache_get_configuration(); + $directives = $configuration['directives']; + $configName = request()->isCli() ? 'opcache.enable_cli' : 'opcache.enable'; + if (!$directives[$configName]) { + throw new Exception("Please make sure {$configName} is turned on, Get help:http://forum.fastadmin.net/d/1321"); + } + } else { + throw new Exception("Please make sure opcache already enabled, Get help:http://forum.fastadmin.net/d/1321"); + } + } + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; $files = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY + new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY ); - foreach ($files as $name => $file) - { - if (!$file->isDir()) - { + foreach ($files as $name => $file) { + if (!$file->isDir()) { $filePath = $file->getRealPath(); $classes[] = $this->get_class_from_file($filePath); } @@ -90,12 +99,12 @@ class Api extends Command 'author' => $author, 'description' => '', 'apiurl' => $url, + 'language' => $language, ]; $builder = new Builder($classes); $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); - if (!file_put_contents($output_file, $content)) - { + if (!file_put_contents($output_file, $content)) { throw new Exception('Cannot save the content to ' . $output_file); } $output->info("Build Successed!"); @@ -103,7 +112,7 @@ class Api extends Command /** * get full qualified class name - * + * * @param string $path_to_file * @author JBYRNE http://jarretbyrne.com/2015/06/197/ * @return string @@ -120,34 +129,27 @@ class Api extends Command $getting_namespace = $getting_class = false; //Go through each token and evaluate it as necessary - foreach (token_get_all($contents) as $token) - { + foreach (token_get_all($contents) as $token) { //If this token is the namespace declaring, then flag that the next tokens will be the namespace name - if (is_array($token) && $token[0] == T_NAMESPACE) - { + if (is_array($token) && $token[0] == T_NAMESPACE) { $getting_namespace = true; } //If this token is the class declaring, then flag that the next tokens will be the class name - if (is_array($token) && $token[0] == T_CLASS) - { + if (is_array($token) && $token[0] == T_CLASS) { $getting_class = true; } //While we're grabbing the namespace name... - if ($getting_namespace === true) - { + if ($getting_namespace === true) { //If the token is a string or the namespace separator... - if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) - { + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { //Append the token's value to the name of the namespace $namespace .= $token[1]; - } - else if ($token === ';') - { + } else if ($token === ';') { //If the token is the semicolon, then we're done with the namespace declaration $getting_namespace = false; @@ -155,12 +157,10 @@ class Api extends Command } //While we're grabbing the class name... - if ($getting_class === true) - { + if ($getting_class === true) { //If the token is a string, it's the name of the class - if (is_array($token) && $token[0] == T_STRING) - { + if (is_array($token) && $token[0] == T_STRING) { //Store the token's value as the class name $class = $token[1]; diff --git a/application/admin/command/Api/library/Builder.php b/application/admin/command/Api/library/Builder.php old mode 100644 new mode 100755 index 6769a913..264610aa --- a/application/admin/command/Api/library/Builder.php +++ b/application/admin/command/Api/library/Builder.php @@ -31,7 +31,7 @@ class Builder public function __construct($classes = []) { $this->classes = array_merge($this->classes, $classes); - $this->view = \think\View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); } protected function extractAnnotations() diff --git a/application/admin/command/Api/template/index.html b/application/admin/command/Api/template/index.html old mode 100644 new mode 100755 index 264eae1c..0f545a79 --- a/application/admin/command/Api/template/index.html +++ b/application/admin/command/Api/template/index.html @@ -1,5 +1,5 @@ - +
diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php index 3ba5c204..61772f96 100644 --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -11,6 +11,7 @@ use think\console\Output; use think\Db; use think\Exception; use think\Lang; +use think\Loader; class Crud extends Command { @@ -105,32 +106,34 @@ class Crud extends Command protected function configure() { $this - ->setName('crud') - ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) - ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) - ->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', 'belongsto') - ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) - ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) - ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) - ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) - ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) - ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) - ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) - ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) - ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) - ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) - ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) - ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) - ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) - ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) - ->setDescription('Build CRUD controller and model from table'); + ->setName('crud') + ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) + ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) + ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) + ->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) + ->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null) + ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null) + ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null) + ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation primary key', null) + ->addOption('relationfields', 's', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table fields', null) + ->addOption('relationmode', 'o', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table mode,hasone or belongsto', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) + ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) + ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) + ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) + ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) + ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) + ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) + ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) + ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) + ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) + ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) + ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) + ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) + ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) + ->setDescription('Build CRUD controller and model from table'); } protected function execute(Input $input, Output $output) @@ -142,12 +145,13 @@ class Crud extends Command $controller = $input->getOption('controller'); //自定义模型 $model = $input->getOption('model'); + //自定义显示字段 + $fields = $input->getOption('fields'); //强制覆盖 $force = $input->getOption('force'); //是否为本地model,为0时表示为全局model将会把model放在app/common/model中 $local = $input->getOption('local'); - if (!$table) - { + if (!$table) { throw new Exception('table name can\'t empty'); } //是否生成菜单 @@ -157,11 +161,13 @@ class Crud extends Command //自定义关联表模型 $relationModel = $input->getOption('relationmodel'); //模式 - $mode = $input->getOption('mode'); + $relationMode = $mode = $input->getOption('relationmode'); //外键 $relationForeignKey = $input->getOption('relationforeignkey'); //主键 $relationPrimaryKey = $input->getOption('relationprimarykey'); + //关联表显示字段 + $relationFields = $input->getOption('relationfields'); //复选框后缀 $setcheckboxsuffix = $input->getOption('setcheckboxsuffix'); //单选框后缀 @@ -211,49 +217,74 @@ class Crud extends Command if ($sortfield) $this->sortField = $sortfield; - //如果有启用关联模式 - 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'); //检查主表 - $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; - $modelTableName = $tableName = $table; + $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; $modelTableType = 'table'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { - $tableName = $prefix . $table; + $modelTableTypeName = $modelTableName = $modelName; + $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); + if (!$modelTableInfo) { $modelTableType = 'name'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { + $modelTableName = $prefix . $modelName; + $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); + if (!$modelTableInfo) { throw new Exception("table not found"); } } - $tableInfo = $tableInfo[0]; + $modelTableInfo = $modelTableInfo[0]; - $relationModelTableName = $relationTableName = $relation; - $relationModelTableType = 'table'; + $relations = []; //检查关联表 - if ($relation) - { - $relation = stripos($relation, $prefix) === 0 ? substr($relation, strlen($prefix)) : $relation; - $relationModelTableName = $relationTableName = $relation; - $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - $relationTableName = $prefix . $relation; - $relationModelTableType = 'name'; + if ($relation) { + $relationArr = $relation; + $relations = []; + + foreach ($relationArr as $index => $relationTable) { + $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable; + $relationTableType = 'table'; + $relationTableTypeName = $relationTableName = $relationName; $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - throw new Exception("relation table not found"); + if (!$relationTableInfo) { + $relationTableType = 'name'; + $relationTableName = $prefix . $relationName; + $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); + if (!$relationTableInfo) { + throw new Exception("relation table not found"); + } } + $relationTableInfo = $relationTableInfo[0]; + $relationModel = isset($relationModel[$index]) ? $relationModel[$index] : ''; + + //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 + $relationName = $this->getModelName($relationModel, $relationName); + $relationFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationName . '.php'; + + $relations[] = [ + //关联表基础名 + 'relationName' => $relationName, + //关联模型名 + 'relationModel' => $relationModel, + //关联文件 + 'relationFile' => $relationFile, + //关联表名称 + 'relationTableName' => $relationTableName, + //关联表信息 + 'relationTableInfo' => $relationTableInfo, + //关联模型表类型(name或table) + 'relationTableType' => $relationTableType, + //关联模型表类型名称 + 'relationTableTypeName' => $relationTableTypeName, + //关联模式 + 'relationFields' => isset($relationFields[$index]) ? explode(',', $relationFields[$index]) : [], + //关联模式 + 'relationMode' => isset($relationMode[$index]) ? $relationMode[$index] : 'belongsto', + //关联表外键 + 'relationForeignKey' => isset($relationForeignKey[$index]) ? $relationForeignKey[$index] : Loader::parseName($relationName) . '_id', + //关联表主键 + 'relationPrimaryKey' => isset($relationPrimaryKey[$index]) ? $relationPrimaryKey[$index] : '', + ]; } } @@ -284,27 +315,21 @@ class Crud extends Command $validateFile = $adminPath . 'validate' . DS . $modelName . '.php'; - //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 - $relationModelName = $this->getModelName($relationModel, $relation); - $relationModelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationModelName . '.php'; - //是否为删除模式 $delete = $input->getOption('delete'); - if ($delete) - { + if ($delete) { $readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $langFile, $javascriptFile]; - foreach ($readyFiles as $k => $v) - { + foreach ($readyFiles as $k => $v) { $output->warning($v); } - $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); - $line = fgets(STDIN); - if (trim($line) != 'yes') - { - throw new Exception("Operation is aborted!"); + if (!$force) { + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } } - foreach ($readyFiles as $k => $v) - { + foreach ($readyFiles as $k => $v) { if (file_exists($v)) unlink($v); } @@ -314,20 +339,17 @@ class Crud extends Command } //非覆盖模式时如果存在控制器文件则报错 - if (is_file($controllerFile) && !$force) - { + if (is_file($controllerFile) && !$force) { throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在模型文件则报错 - if (is_file($modelFile) && !$force) - { + if (is_file($modelFile) && !$force) { throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在验证文件则报错 - if (is_file($validateFile) && !$force) - { + if (is_file($validateFile) && !$force) { throw new Exception("validate already exists!\nIf you need to rebuild again, use the parameter --force=true "); } @@ -335,26 +357,43 @@ class Crud extends Command //从数据库中获取表字段信息 $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]); - } - + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = Db::query($sql, [$dbname, $modelTableName]); $fieldArr = []; - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $fieldArr[] = $v['COLUMN_NAME']; } - $relationFieldArr = []; - foreach ($relationColumnList as $k => $v) - { - $relationFieldArr[] = $v['COLUMN_NAME']; + // 加载关联表的列 + foreach ($relations as $index => &$relation) { + $relationColumnList = Db::query($sql, [$dbname, $relation['relationTableName']]); + + $relationFieldList = []; + foreach ($relationColumnList as $k => $v) { + $relationFieldList[] = $v['COLUMN_NAME']; + } + if (!$relation['relationPrimaryKey']) { + foreach ($relationColumnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { + $relation['relationPrimaryKey'] = $v['COLUMN_NAME']; + break; + } + } + } + // 如果主键为空 + if (!$relation['relationPrimaryKey']) { + throw new Exception('Relation Primary key not found!'); + } + // 如果主键不在表字段中 + if (!in_array($relation['relationPrimaryKey'], $relationFieldList)) { + throw new Exception('Relation Primary key not found in table!'); + } + $relation['relationColumnList'] = $relationColumnList; + $relation['relationFieldList'] = $relationFieldList; } + unset($relation); $addList = []; $editList = []; @@ -364,70 +403,47 @@ class Crud extends Command $order = 'id'; $priDefined = FALSE; $priKey = ''; - $relationPriKey = ''; - foreach ($columnList as $k => $v) - { - if ($v['COLUMN_KEY'] == 'PRI') - { + $relationPrimaryKey = ''; + foreach ($columnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { $priKey = $v['COLUMN_NAME']; break; } } - if (!$priKey) - { + if (!$priKey) { throw new Exception('Primary key not found!'); } - 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); - } - } - } + foreach ($relations as $index => &$relation) { + if ($relation['relationMode'] == 'hasone') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; - try - { + if (!in_array($relationForeignKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } else { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : Loader::parseName($relation['relationName']) . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $relation['relationPriKey']; + if (!in_array($relationForeignKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } + $relation['relationForeignKey'] = $relationForeignKey; + $relation['relationPrimaryKey'] = $relationPrimaryKey; + } + unset($relation); + + try { Form::setEscapeHtml(false); $setAttrArr = []; $getAttrArr = []; @@ -436,26 +452,26 @@ class Crud extends Command $controllerAssignList = []; //循环所有字段,开始构造视图的HTML和JS信息 - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $field = $v['COLUMN_NAME']; $itemArr = []; // 这里构建Enum和Set类型的列表数据 - if (in_array($v['DATA_TYPE'], ['enum', 'set'])) - { + if (in_array($v['DATA_TYPE'], ['enum', 'set', 'tinyint'])) { $itemArr = substr($v['COLUMN_TYPE'], strlen($v['DATA_TYPE']) + 1, -1); $itemArr = explode(',', str_replace("'", '', $itemArr)); $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + //如果类型为tinyint且有使用备注数据 + if ($itemArr && $v['DATA_TYPE'] == 'tinyint') { + $v['DATA_TYPE'] = 'enum'; + } } // 语言列表 - if ($v['COLUMN_COMMENT'] != '') - { + if ($v['COLUMN_COMMENT'] != '') { $langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']); } $inputType = ''; //createtime和updatetime是保留字段不能修改和添加 - if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) - { + if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) { $inputType = $this->getFieldType($v); // 如果是number类型时增加一个步长 @@ -467,17 +483,14 @@ class Crud extends Command $defaultValue = $v['COLUMN_DEFAULT']; $editValue = "{\$row.{$field}}"; // 如果默认值非null,则是一个必选项 - if ($v['IS_NULLABLE'] == 'NO') - { + if ($v['IS_NULLABLE'] == 'NO') { $attrArr['data-rule'] = 'required'; } - if ($inputType == 'select') - { + if ($inputType == 'select') { $cssClassArr[] = 'selectpicker'; $attrArr['class'] = implode(' ', $cssClassArr); - if ($v['DATA_TYPE'] == 'set') - { + if ($v['DATA_TYPE'] == 'set') { $attrArr['multiple'] = ''; $fieldName .= "[]"; } @@ -488,23 +501,19 @@ class Crud extends Command $itemArr = $this->getLangArray($itemArr, FALSE); //添加一个获取器 $this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); - if ($v['DATA_TYPE'] == 'set') - { + if ($v['DATA_TYPE'] == 'set') { $this->setAttr($setAttrArr, $field, $inputType); } $this->appendAttr($appendAttrList, $field); $formAddElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formEditElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); - } - else if ($inputType == 'datetime') - { + } else if ($inputType == 'datetime') { $cssClassArr[] = 'datetimepicker'; $attrArr['class'] = implode(' ', $cssClassArr); $format = "YYYY-MM-DD HH:mm:ss"; $phpFormat = "Y-m-d H:i:s"; $fieldFunc = ''; - switch ($v['DATA_TYPE']) - { + switch ($v['DATA_TYPE']) { case 'year'; $format = "YYYY"; $phpFormat = 'Y'; @@ -536,9 +545,7 @@ class Crud extends Command $fieldFunc = $fieldFunc ? "|{$fieldFunc}" : ""; $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); $formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr); - } - else if ($inputType == 'checkbox' || $inputType == 'radio') - { + } else if ($inputType == 'checkbox' || $inputType == 'radio') { unset($attrArr['data-rule']); $fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName; $attrArr['name'] = "row[{$fieldName}]"; @@ -547,8 +554,7 @@ class Crud extends Command $itemArr = $this->getLangArray($itemArr, FALSE); //添加一个获取器 $this->getAttr($getAttrArr, $field, $inputType); - if ($inputType == 'checkbox') - { + if ($inputType == 'checkbox') { $this->setAttr($setAttrArr, $field, $inputType); } $this->appendAttr($appendAttrList, $field); @@ -556,25 +562,18 @@ class Crud extends Command $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); - } - else if ($inputType == 'textarea') - { + } else if ($inputType == 'textarea') { $cssClassArr[] = substr($field, -7) == 'content' ? $this->editorClass : ''; $attrArr['class'] = implode(' ', $cssClassArr); $attrArr['rows'] = 5; $formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr); $formEditElement = Form::textarea($fieldName, $editValue, $attrArr); - } - else if ($inputType == 'switch') - { + } else if ($inputType == 'switch') { unset($attrArr['data-rule']); - if ($defaultValue === '1' || $defaultValue === 'Y') - { + if ($defaultValue === '1' || $defaultValue === 'Y') { $yes = $defaultValue; $no = $defaultValue === '1' ? '0' : 'N'; - } - else - { + } else { $no = $defaultValue; $yes = $defaultValue === '0' ? '1' : 'Y'; } @@ -583,20 +582,15 @@ class Crud extends Command $formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class' => 'control-label']), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr)); $formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class' => 'control-label']), Form::checkbox($fieldName, $yes, 0, $attrArr)); $formEditElement = str_replace('type="checkbox"', 'type="checkbox" {in name="' . "\$row.{$field}" . '" value="' . $yes . '"}checked{/in}', $formEditElement); - } - else if ($inputType == 'citypicker') - { + } else if ($inputType == 'citypicker') { $attrArr['class'] = implode(' ', $cssClassArr); $attrArr['data-toggle'] = "city-picker"; $formAddElement = sprintf("卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!
如有重要数据请备份后再操作!', 'Upgrade tips' => '确认升级插件?如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!
升级后可能出现部分冗余数据记录,请根据需要移除即可!!!