-
-
Notifications
You must be signed in to change notification settings - Fork 192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problems with Model generator #429
Comments
Without making changes to Yii source code, you should solve adding a new rule based on default validator:
to be used only with NOT NULL field but with a DEFAULT value. Next, Gii generator could be updated to produces this rule. |
Ок, one more case. If field UNSIGNED
by default generator no rule fo this. I can put -1 in form and have error
|
I have quickly read yii2 gii source code and found where rules are created: yii2-gii/src/generators/model/Generator.php Line 378 in aa7a821
and there is not specific code for I'll make a PR updating this part to support default and unsgined values. |
Generate advanced rules: public function generateRules($table)
{
$columns = [];
foreach ($table->columns as $index => $column) {
$isBlameableCol = ($column->name === $this->createdByColumn || $column->name === $this->updatedByColumn);
$isTimestampCol = ($column->name === $this->createdAtColumn || $column->name === $this->updatedAtColumn);
$removeCol = ($this->useBlameableBehavior && $isBlameableCol)
|| ($this->useTimestampBehavior && $isTimestampCol);
if ($removeCol) {
$columns[$index] = $column;
unset($table->columns[$index]);
}
}
$rules = [];
//for enum fields create rules "in range" for all enum values
$enum = $this->getEnum($table->columns);
foreach ($enum as $field_name => $field_details) {
$ea = array();
foreach ($field_details['values'] as $field_enum_values) {
$ea[] = 'self::'.$field_enum_values['const_name'];
}
$rules['enum-' . $field_name] = "['".$field_name."', 'in', 'range' => [\n ".implode(
",\n ",
$ea
).",\n ]\n ]";
}
$rules = array_merge($rules, $this->rulesIntegerMinMax($table));
// inject namespace for targetClass
$modTable = clone $table;
$intColumn = 'nonecolumn';
foreach($modTable->columns as $key => $column){
switch ($column->type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
case Schema::TYPE_TINYINT:
case 'mediumint':
$modTable->columns[$key]->type = Schema::TYPE_INTEGER;
$intColumn = $column->name;
}
}
$parentRules = parent::generateRules($modTable);
$ns = "\\{$this->ns}\\";
$match = "'targetClass' => ";
$replace = $match.$ns;
foreach ($parentRules as $k => $parentRule) {
if(preg_match('#\'' . $intColumn . '\',.*\'string\', \'max\' => 5#',$parentRule)){
unset($parentRules[$k]);
continue;
}
if(preg_match('#\'integer\']$#',$parentRule)){
unset($parentRules[$k]);
continue;
}
$parentRules[$k] = str_replace($match, $replace, $parentRule);
}
$rules = array_merge($rules,$parentRules);
$table->columns = array_merge($table->columns, $columns);
return $rules;
}
/**
* Generates validation min max rules.
*
* @param TableSchema $table the table schema
*
* @return array the generated validation rules
*/
public function rulesIntegerMinMax($table): array
{
$UNSIGNED = 'Unsigned';
$SIGNED = 'Signed';
$rules = [
Schema::TYPE_TINYINT . ' ' . $UNSIGNED => [
[],
'integer',
'min' => 0,
'max' => 255,
],
Schema::TYPE_TINYINT . ' ' . $SIGNED => [
[],
'integer',
'min' => -128,
'max' => 127,
],
Schema::TYPE_SMALLINT . ' ' . $UNSIGNED => [
[],
'integer',
'min' => 0,
'max' => 65535,
],
Schema::TYPE_SMALLINT . ' ' . $SIGNED => [
[],
'integer',
'min' => -32768,
'max' => 32767,
],
'mediumint' . ' ' . $UNSIGNED => [
[],
'integer',
'min' => 0,
'max' => 16777215,
],
'mediumint' . ' ' . $SIGNED => [
[],
'integer',
'min' => -8388608,
'max' => 8388607,
],
Schema::TYPE_INTEGER . ' ' . $UNSIGNED => [
[],
'integer',
'min' => 0,
'max' => 4294967295,
],
Schema::TYPE_INTEGER . ' ' . $SIGNED => [
[],
'integer',
'min' => -2147483648,
'max' => 2147483647,
],
Schema::TYPE_BIGINT . ' ' . $UNSIGNED => [
[],
'integer',
'min' => 0,
'max' => 0xFFFFFFFFFFFFFFFF,
],
Schema::TYPE_BIGINT . ' ' . $SIGNED => [
[],
'integer',
'min' => -0xFFFFFFFFFFFFFFF,
'max' => 0xFFFFFFFFFFFFFFE,
],
];
foreach ($table->columns as $column) {
$key = $column->type . ' ' . ($column->unsigned? $UNSIGNED : $SIGNED);
if(!isset($rules[$key])){
continue;
}
$rules[$key][0][] = $column->name;
}
/**
* remove empty rules
*/
foreach($rules as $ruleName => $rule){
if(!$rule[0]){
unset($rules[$ruleName]);
continue;
}
$rules[$ruleName] = '['
. '[\'' . implode('\',\'',$rule[0]) . '\']'
. ',\'integer\''
. ' ,\'min\' => ' . $rule['min']
. ' ,\'max\' => ' . $rule['max']
. ']';
}
return $rules;
} |
Is this code available in some branch (to be tested) ? |
It is in my private repository. Can give access. |
Great, so you could create a PR starting from your local branch. Have you tried to launch the tests suite? |
Check email. |
@FabrizioCaldarelli maybe |
Additionaly can add enum: generator.ph 'enum' => $this->getEnum($tableSchema->columns),
//..........................
/**
* prepare ENUM field values.
*
* @param array $columns
*
* @return array
*/
public function getEnum($columns)
{
$enum = [];
foreach ($columns as $column) {
if (!$this->isEnum($column)) {
continue;
}
$column_camel_name = str_replace(' ', '', ucwords(implode(' ', explode('_', $column->name))));
$enum[$column->name]['func_opts_name'] = 'opts'.$column_camel_name;
$enum[$column->name]['func_get_label_name'] = 'get'.$column_camel_name.'ValueLabel';
$enum[$column->name]['isFunctionPrefix'] = 'is'.$column_camel_name;
$enum[$column->name]['columnName'] = $column->name;
$enum[$column->name]['values'] = [];
$enum_values = explode(',', substr($column->dbType, 4, strlen($column->dbType) - 1));
foreach ($enum_values as $value) {
$value = trim($value, "()'");
$const_name = strtoupper($column->name.'_'.$value);
$const_name = preg_replace('/\s+/', '_', $const_name);
$const_name = str_replace(['-', '_', ' '], '_', $const_name);
$const_name = preg_replace('/[^A-Z0-9_]/', '', $const_name);
$label = Inflector::camel2words($value);
$enum[$column->name]['values'][] = [
'value' => $value,
'const_name' => $const_name,
'label' => $label,
'isFunctionSuffix' => str_replace(' ', '', ucwords(implode(' ', explode('_', $value))))
];
}
}
return $enum;
}
/**
* validate is ENUM.
*
* @param $column table column
*
* @return type
*/
public function isEnum($column)
{
return substr(strtoupper($column->dbType), 0, 4) == 'ENUM';
} template
<?php
if(!empty($enum)){
?>
/**
* ENUM field values
*/
<?php
foreach($enum as $column_name => $column_data){
foreach ($column_data['values'] as $enum_value){
echo ' public const ' . $enum_value['const_name'] . ' = \'' . $enum_value['value'] . '\';' . PHP_EOL;
}
}
}
//................
foreach($enum as $column_name => $column_data){
?>
/**
* get column <?php echo $column_name?> enum value label
* @param string $value
* @return string
*/
public static function <?php echo $column_data['func_get_label_name']?>($value): string
{
if(!$value){
return '';
}
$labels = self::<?php echo $column_data['func_opts_name']?>();
return $labels[$value] ?? $value;
}
/**
* column <?php echo $column_name?> ENUM value labels
* @return array
*/
public static function <?php echo $column_data['func_opts_name']?>(): array
{
return [
<?php
foreach($column_data['values'] as $k => $value){
if ($generator->enableI18N) {
echo ' '.'self::' . $value['const_name'] . ' => Yii::t(\'' . $generator->messageCategory . '\', \'' . $value['value'] . "'),\n";
} else {
echo ' '.'self::' . $value['const_name'] . ' => \'' . $column_data['columnName'] . "',\n";
}
}
?>
];
}
<?php
}
if(!empty($enum)){
?>
/**
* ENUM field values
*/
<?php
foreach($enum as $column_name => $column_data){
foreach ($column_data['values'] as $enum_value){
?>
/**
* @return bool
*/
public function <?=$column_data['isFunctionPrefix'].$enum_value['isFunctionSuffix']?>(): bool
{
return $this-><?=$column_name?> === self::<?=$enum_value['const_name']?>;
}
<?php
}
}
} |
What steps will reproduce the problem?
At documentation Getting Started for example next SQL
Look at last column
population
- it`s not null and has default value.Next in start gii generating Model
Country
with next rulespopulation
is not required and has no default value.Next step in CRUD make editor. In Add object form - population is not required filed, but when it`s blank I have SQL Error
What is the expected result?
For this case good SQL is without empty form field
INSERT INTO `country` (`code`, `name`) VALUES ('44', '44')
or with default value from scheme
INSERT INTO `country` (`code`, `name`, `population`) VALUES ('44', '44', '0')
Additional info
The text was updated successfully, but these errors were encountered: