thinkphp 自定义命令生成验证器文件
命令demo
php think hello(指令) --table 表名
代码如下
<?php declare (strict_types=1); namespace app\command; use DateTime; use think\console\Command; use think\console\Input; use think\console\input\Option; use think\console\Output; use think\facade\Db; use think\helper\Str; class Hello extends Command { protected $rules = []; #命名空间 private $namespace = 'app\\validate'; #文件名称 private $fileName = ''; protected function setFileName() { $this->fileName = Str::studly($this->table) . "Validate"; } protected function configure() { // 指令配置 $this->setName('hello') ->addOption('table', null, Option::VALUE_REQUIRED, '表名') ->setDescription('自动生成验证器'); } protected function execute(Input $input, Output $output) { try { $this->namespace = 'app\\validate'; if (!$input->hasOption('table')) { throw new \think\Exception('--table参数不能为空', 10006); } $this->table = $input->getOption('table'); $this->setFileName(); $database = config('database.connections.mysql.database'); $tableArr = Db::query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{$database}' and TABLE_NAME = '{$this->table}';"); if (0 === count($tableArr)) { throw new \think\Exception($this->table . "表不存在", 10006); } $comments = Db::query("SHOW FULL COLUMNS FROM `{$this->table}`"); foreach ($comments as $v) { if ('PRI' === $v['Key']) continue; $this->rules[$v['Field']] = ['name' => $this->getColumnName($v), "rule" => []]; $columnFullType = $v['Type']; $preg = "/^(\w+)\(?/"; if (preg_match($preg, $columnFullType, $matches)) { $columnType = $matches[1]; } else { throw new \think\Exception('未找到字段类型', 10006); } // if ('NO' === $v['Null']) { // $this->rules[$v['Field']]['rule'][] = 'require'; // } $this->rules[$v['Field']]['rule'][] = 'require'; if ($this->containsPhoneOrMobile($v['Comment'])) { $this->rules[$v['Field']]['rule'][] = 'mobile'; } if ($this->email($v['Comment'])) { $this->rules[$v['Field']]['rule'][] = 'email'; } switch ($columnType) { case 'int': case 'bigint': case 'tinyint': $this->setIntColumnRule($v, $columnType); break; case 'char': case 'varchar': $this->setCharColumnRule($v, $columnType); break; case 'tinytext': case 'text': case 'mediumtext': case 'longtext': $this->setTextColumnRule($v, $columnType); break; case 'float': case 'double': throw new \think\Exception('浮点型只支持decimal类型', 10006); case 'decimal': $this->setFloatColumnRule($v, $columnType); break; case 'date': $this->rules[$v['Field']]['rule'][] = 'checkDateYmd:' . $this->getColumnName($v) . '时间格式不对'; break; case 'datetime': $this->rules[$v['Field']]['rule'][] = 'checkDateYmdHis:' . $this->getColumnName($v) . '时间格式不对'; break; default: throw new \think\Exception("未找到字段{$columnType}类型请完善", 10006); } } $content = $this->generateValidatorContent(); !is_dir($this->namespace) && mkdir($this->namespace, 0755, true); $pathname = $this->namespace . '\\' . $this->fileName; if (is_file($pathname . '.php')) { throw new \think\Exception("该文件已经存在" . $pathname. '.php', 10006); } file_put_contents($pathname . '.php', $content); $output->writeln("创建成功"); } catch (\Throwable $e) { $output->writeln($e->getMessage()); } } protected function setIntColumnRule($v, $columnType) { if ($this->unsigned($v['Type'])) { $this->rules[$v['Field']]['rule'][] = 'egt:0'; $this->rules[$v['Field']]['rule'][] = 'number'; $this->rules[$v['Field']]['rule'][] = 'between:' . $this->getIntBetween($columnType, 'unsignen'); } else { $this->rules[$v['Field']]['rule'][] = 'integer'; $this->rules[$v['Field']]['rule'][] = 'between:' . $this->getIntBetween($columnType, 'signen'); } } protected function setTextColumnRule($v, $columnType) { $this->rules[$v['Field']]['rule'][] = $this->getTextBetween($columnType); } function containsPhoneOrMobile($str) { $lowerStr = strtolower($str); if (strpos($lowerStr, 'phone') !== false) { return true; } if (mb_strpos($lowerStr, '手机号') !== false) { return true; } return false; } function email($str) { $lowerStr = strtolower($str); if (strpos($lowerStr, 'email') !== false) { return true; } if (mb_strpos($lowerStr, '邮箱') !== false) { return true; } return false; } protected function setCharColumnRule($v, $columnType) { $preg = "/^(?:var)?char\((\d+)\)/"; if (preg_match($preg, $v['Type'], $matches)) { $charLength = $matches[1]; } else { throw new \think\Exception('char未匹配到长度', 10006); } $this->rules[$v['Field']]['rule'][] = 'length:0,' . $charLength; } protected function setFloatColumnRule($v, $columnType) { $preg = "/^decimal\((\d+),(\d+)\)/"; if (preg_match($preg, $v['Type'], $matches)) { if ("2" !== $matches[2]) { throw new \think\Exception('decimal必须小数点两位', 10006); } $float = $matches[1]; } else { throw new \think\Exception('decimal不能正确匹配', 10006); } $columnName = $this->getColumnName($v); if ($this->unsigned($v['Type'])) { $this->rules[$v['Field']]['rule'][] = "checkFloat2:{$float}@{$columnName}小数点后面最多两位的数字"; } else { $this->rules[$v['Field']]['rule'][] = "checkUnsignedFloat2:{$float}@{$columnName}小数点后面最多两位的数字"; } } public function getIntBetween($type, $unsignen) { if (isset($this->intArrBetween[$type][$unsignen])) { return $this->intArrBetween[$type][$unsignen]; } throw new \think\Exception('未找到' . $type . '字段类型的取值范围', 10006); } public function getTextBetween($type) { if (isset($this->textArrBetween [$type])) { return $this->textArrBetween [$type]; } throw new \think\Exception('未找到' . $type . '字段类型的取值范围', 10006); } protected function unsigned($string) { if (strpos($string, 'unsigned') !== false) return true; return false; } protected function getColumnName($columnData) { $Comment = $columnData['Comment']; $preg = "/^([^-]*)-?/"; if (preg_match($preg, $Comment, $matches)) { return $matches[1] === '' ? $columnData['Field'] : $matches[1]; } else { return $columnData['Field']; } } protected function generateValidatorContent() { $ruleStrContent = <<<RULE protected \$rule = [ REPLACE ];\n RULE; $messageStrContent = <<<CONTENT protected \$message = [ REPLACE ];\n CONTENT; $ruleStr = ''; $contentStr = ''; foreach ($this->rules as $k => $v) { $ruleContent = implode("|", $v['rule']); $ruleStr .= "\t\t\t'{$k}' => '{$ruleContent}',#{$v['name']}\n"; foreach ($v['rule'] as $key => $value) { $ruleMsg = explode(':', $value); $msg = ''; switch ($ruleMsg[0]) { case 'number': $msg = "必须是正整数"; break; case 'date': $msg = "日期格式不对"; break; case 'between': $fanwei = str_replace(',', '~', $ruleMsg[1]); $msg = "必须在{$fanwei}内"; break; case 'integer': $msg = "必须为整数"; break; case 'egt': $msg = "大于等于0"; break; case 'require': $msg = "不能为空"; break; case 'mobile': $msg = "格式错误"; break; case 'email': $msg = "格式错误"; break; } if ($msg) { $contentStr .= "\t\t\t'{$k}.{$ruleMsg[0]}' => '{$v['name']}{$msg}',#{$v['name']}\n"; } } } $ruleStr = str_replace('REPLACE', $ruleStr, $ruleStrContent); $messageStr = str_replace('REPLACE', $contentStr, $messageStrContent); return "<?php namespace $this->namespace; use DateTime; use think\Validate; class " . Str::studly($this->table) . "Validate extends Validate { $ruleStr $messageStr public function sceneAdd() { return \$this->only([]); } public function sceneEdit() { return \$this->only([]); } protected function checkFloat2(\$value, \$rule, \$data=[]) { \$arr = explode('@',\$rule); \$pattern = '/^(0|[1-9]\d*)(\.\d{1,2})?$/'; if (!preg_match(\$pattern, \$value)) { return \$arr[1]; } if(strlen((int)\$value)>(\$arr[0]-2)){ return \$arr[1]; } return true; } protected function checkUnsignedFloat2(\$value, \$rule, \$data=[]) { \$arr = explode('@',\$rule); \$pattern = '/^(?:\-)?(0|[1-9]\d*)(\.\d{1,2})?$/'; if (!preg_match(\$pattern, \$value)) { return \$arr[1]; } if(strlen((int)\$value)>(\$arr[0]-2)){ return \$arr[1]; } return true; } protected function checkDateYmd(\$value, \$rule, \$data = []) { DateTime::createFromFormat('Y-m-d', \$value); \$errors = DateTime::getLastErrors(); if ((\$errors['warning_count'] + \$errors['error_count']) > 0) { return \$rule; } return true; } protected function checkDateYmdHis(\$value, \$rule, \$data = []) { DateTime::createFromFormat('Y-m-d H:i:s', \$value); \$errors = DateTime::getLastErrors(); if ((\$errors['warning_count'] + \$errors['error_count']) > 0) { return \$rule; } return true; } } "; } private $intArrBetween = [ 'int' => [ 'unsignen' => '0,4294967295', 'signen' => "-2147483648,2147483647", ], 'bigint' => [ 'unsignen' => '0,12345678901234567890', 'signen' => "-1234567890123456789,1234567890123456789", ] , 'tinyint' => [ 'unsignen' => '0,255', 'signen' => "-128,127", ] ]; private $textArrBetween = [ 'tinytext' => '0,255', 'text' => '0,65535', 'mediumtext' => '0,16777215', 'longtext' => '0,4294967295', ]; }