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',
];
}