Skip to content
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

Model rules for default values #420

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions src/generators/model/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,64 @@ public function generateLabels($table)
* Generates validation rules for the specified table.
* @param \yii\db\TableSchema $table the table schema
* @return array the generated validation rules
* @throws NotSupportedException
*/
public function generateRules($table)
{
$types = [];
$lengths = [];
$rules = [];
$driverName = $this->getDbDriverName();

if (in_array($driverName, ['mysql', 'sqlite'], true)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why only that's drivers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know only MySql.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure just about MySql

Copy link
Contributor

@WinterSilence WinterSilence Apr 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn that's types normalized check DB subfolders, also:

/**
 * @var string abstract type of this column. Possible abstract types include:
 * char, string, text, boolean, smallint, integer, bigint, float, decimal, datetime,
 * timestamp, time, date, binary, and money.
 */
public $type;
/**
 * @var string the PHP type of this column. Possible PHP types include:
 * `string`, `boolean`, `integer`, `double`, `array`.
 */
public $phpType;

as you can see, $phpType use only 5 types, you can detect numeric as if (in_array($column->phpType, ['integer', 'double', 'float'], true)) 'float' added because php dont have type 'double', i think it's used to set validation rule


$db = $this->getDbConnection();
$columnsDefaultNull = [];
$columnsDefaultValues = [];

foreach ($table->columns as $column) {

if ($column->defaultValue !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ($column->defaultValue === null && !$column->allowNull) { continue; }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not need "&& !$column->allowNull"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn no need $columnsDefaultNull set if ($column->allowNull) {$defaultValue = 'null';}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default all attributes has null value. Not need.

Copy link
Contributor

@WinterSilence WinterSilence Apr 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn null !== 'null', echo null or echo false don't print null or false, in my packages I use <?=json_encode($var)?>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked MySql schema. If in MySql field no default value, then $column->defaultValue is null.
$column->allowNull use for rules "required"
Correct:

 if ($column->defaultValue === null) { continue; }

Copy link
Contributor

@WinterSilence WinterSilence Apr 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn $column->allowNull is @param type|null $foo, function setFoo(?type $value) and function getFoo(): ?type.

If in MySql field no default value, then $column->defaultValue is null.

CREATE TABLE `demo` (`a` VARCHAR(255) NULL, `b` TEXT NULL);
INSERT INTO `demo` (`a`) VALUES (NULL); // (NULL, NULL)
CREATE TABLE ` demo` (
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    `a` TEXT NULL DEFAULT NULL,
    `b` VARCHAR(50) NULL DEFAULT 'is default'
    PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB;

INSERT INTO ` demo` (`a`, `b`) VALUES ('...', NULL);
INSERT INTO ` demo` (`a`, `b`) VALUES (NULL, '...');
INSERT INTO `demo` (`b`) VALUES (NULL);
INSERT INTO `demo` (`a`) VALUES ('...');

switch ($column->type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
case Schema::TYPE_TINYINT:
case Schema::TYPE_BOOLEAN:
case Schema::TYPE_FLOAT:
case Schema::TYPE_DOUBLE:
case Schema::TYPE_DECIMAL:
case Schema::TYPE_MONEY:
$defaultValue = $column->defaultValue;
break;

case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP:
if(strtoupper($column->defaultValue) === 'CURRENT_TIMESTAMP'){
$defaultValue = 'date(\'Y-m-d H:i:s\')';
Copy link
Contributor

@WinterSilence WinterSilence Apr 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mssql/sql server:

DEFAULT definitions cannot be created on columns with a timestamp data type or columns with an IDENTITY property.

https://docs.microsoft.com/ru-ru/sql/t-sql/statements/create-table-transact-sql?view=sql-server-ver15

also it must be $defaultValue = 'time()';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For timestamp column, if default value not set, fired on require rule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn

if (strtoupper($column->defaultValue) === 'CURRENT_TIMESTAMP') {
     if ($driverName === 'mssql') {
          $defaultValue = $this->generateString($column->defaultValue);
     } else {
        $defaultValue = 'time()';
    }
} 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On MSSQL fired rule, who validate actual field value - time format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uldisn yes, my fail

if (strtoupper($column->defaultValue) === 'CURRENT_TIMESTAMP') {
     if ($driverName === 'mysql' && $column->type === Schema::TYPE_TIMESTAMP) {
          $defaultValue = 'time()';
     } else {
        $defaultValue = "date('Y-m-d H:i:s')";
    }
}

}else{
$defaultValue = $db->getSchema()->quoteValue($column->defaultValue);
}
break;

default:
$defaultValue = $db->getSchema()->quoteValue($column->defaultValue);
}
$columnsDefaultValues[$defaultValue][] = $column->name;
} elseif ($column->allowNull) {
$columnsDefaultNull[] = $column->name;
}
}
foreach($columnsDefaultValues as $defaultValue => $columnNameList){
$rules[] = "[['" . implode("', '", $columnNameList) . "'], 'default', 'value' => $defaultValue]";
}
if ($columnsDefaultNull) {
$rules[] = "[['" . implode("', '", $columnsDefaultNull) . "'], 'default', 'value' => null]";
}
}



foreach ($table->columns as $column) {
if ($column->autoIncrement) {
continue;
Expand Down Expand Up @@ -373,8 +426,6 @@ public function generateRules($table)
}
}
}
$rules = [];
$driverName = $this->getDbDriverName();
foreach ($types as $type => $columns) {
if ($driverName === 'pgsql' && $type === 'integer') {
$rules[] = "[['" . implode("', '", $columns) . "'], 'default', 'value' => null]";
Expand Down
2 changes: 1 addition & 1 deletion tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
Yii::setAlias('@yiiunit/gii', __DIR__);
Yii::setAlias('@yii/gii', dirname(__DIR__) . '/src');

require_once(__DIR__ . '/compatibility.php');
require_once(__DIR__ . '/compatibility.php');
2 changes: 1 addition & 1 deletion tests/data/sqlite.sql
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CREATE TABLE "customer" (
id INTEGER NOT NULL,
email varchar(128) NOT NULL,
name varchar(128),
address text,
address text DEFAULT '-',
status INTEGER DEFAULT 0,
profile_id INTEGER,
PRIMARY KEY (id)
Expand Down
53 changes: 53 additions & 0 deletions tests/generators/ModelGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,57 @@ public function testGenerateProperties($tableName, $columns)
}

}

/**
* @return array
*/
public function rulesDefaultValuesProvider()
{
return [
[
'tableName' => 'customer',
'columns' => [
[
'columnName' => 'name',
'rule' => '[[\'status\'], \'default\', \'value\' => 0]',
],
[
'columnName' => 'address',
'rule' => '[[\'name\', \'profile_id\'], \'default\', \'value\' => null]',
],
[
'columnName' => 'address',
'rule' => '[[\'address\'], \'default\', \'value\' => \'-\']',
],
]
],
];

}

/**
* @dataProvider rulesDefaultValuesProvider
*
* @param string $tableName
* @param array $columns
*/
public function testRulesDefaultValues($tableName, $columns)
{
$generator = new ModelGenerator();
$generator->template = 'default';
$generator->tableName = $tableName;

$files = $generator->generate();

$code = $files[0]->content;
foreach ($columns as $column) {
$location = strpos($code, $column['rule']);
$this->assertTrue(
$location !== false,
"Column \"{$column['columnName']}\" rule should be there:\n" . $column['rule']
);
}

}

}