diff --git a/CHANGELOG.md b/CHANGELOG.md index 113b61433..bdae0719c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - Enh #885: Refactor `AbstractDsn` class (@Tigrov) - Chg #889: Update `AbstractDMLQueryBuilder::insertBatch()` method (@Tigrov) - Enh #890: Add properties of `AbstractColumnSchema` class to constructor (@Tigrov) +- New #899: Add `ColumnSchemaInterface::hasDefaultValue()` and `ColumnSchemaInterface::null()` methods (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/UPGRADE.md b/UPGRADE.md index a22e6df49..2b6379bbc 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -72,9 +72,11 @@ and the following changes were made: - `reference(ForeignKeyConstraint|null $reference)` method is added; - `getReference()` method is added; - `notNull(bool $notNull = true)` method is added; +- `null()` method is added; - `isNotNull()` method is added; - `unique(bool $unique = true)` method is added; - `isUnique()` method is added; +- `hasDefaultValue()` method is added; - all `AbstractColumnSchema` class properties except `$type` moved to constructor; - added `DEFAULT_TYPE` constant to `AbstractColumnSchema` class; - added method chaining. diff --git a/src/QueryBuilder/AbstractColumnDefinitionBuilder.php b/src/QueryBuilder/AbstractColumnDefinitionBuilder.php index 6682de87d..ac08b2032 100644 --- a/src/QueryBuilder/AbstractColumnDefinitionBuilder.php +++ b/src/QueryBuilder/AbstractColumnDefinitionBuilder.php @@ -126,7 +126,7 @@ protected function buildDefault(ColumnSchemaInterface $column): string } if ($column->isAutoIncrement() && $column->getType() !== ColumnType::UUID - || $column->getDefaultValue() === null + || !$column->hasDefaultValue() ) { return ''; } @@ -149,19 +149,15 @@ protected function buildDefaultValue(ColumnSchemaInterface $column): string|null { $value = $column->dbTypecast($column->getDefaultValue()); - if ($value === null) { - return null; - } - - if ($value instanceof ExpressionInterface) { - return $this->queryBuilder->buildExpression($value); - } - /** @var string */ return match (gettype($value)) { GettypeResult::INTEGER => (string) $value, GettypeResult::DOUBLE => (string) $value, GettypeResult::BOOLEAN => $value ? 'TRUE' : 'FALSE', + GettypeResult::NULL => $column->isNotNull() !== true ? 'NULL' : null, + GettypeResult::OBJECT => $value instanceof ExpressionInterface + ? $this->queryBuilder->buildExpression($value) + : $this->queryBuilder->quoter()->quoteValue((string) $value), default => $this->queryBuilder->quoter()->quoteValue((string) $value), }; } @@ -186,7 +182,11 @@ protected function buildExtra(ColumnSchemaInterface $column): string */ protected function buildNotNull(ColumnSchemaInterface $column): string { - return $column->isNotNull() ? ' NOT NULL' : ''; + return match ($column->isNotNull()) { + true => ' NOT NULL', + false => ' NULL', + default => '', + }; } /** diff --git a/src/Schema/Column/AbstractColumnSchema.php b/src/Schema/Column/AbstractColumnSchema.php index 377d0f952..626063a36 100644 --- a/src/Schema/Column/AbstractColumnSchema.php +++ b/src/Schema/Column/AbstractColumnSchema.php @@ -6,9 +6,9 @@ use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\Constant\PhpType; - use Yiisoft\Db\Constraint\ForeignKeyConstraint; +use function array_key_exists; use function property_exists; /** @@ -43,6 +43,11 @@ abstract class AbstractColumnSchema implements ColumnSchemaInterface */ protected const DEFAULT_TYPE = ColumnType::STRING; + /** + * @var mixed $defaultValue The default value of the column. + */ + private mixed $defaultValue; + /** * @var string The column abstract type * @psalm-var ColumnType::* @@ -56,12 +61,11 @@ abstract class AbstractColumnSchema implements ColumnSchemaInterface * @param string|null $comment The column's comment. * @param bool $computed Whether the column is a computed column. * @param string|null $dbType The column's database type. - * @param mixed $defaultValue The default value of the column. * @param array|null $enumValues The list of possible values for an ENUM column. * @param string|null $extra Any extra information that needs to be appended to the column's definition. * @param bool $primaryKey Whether the column is a primary key. * @param string|null $name The column's name. - * @param bool $notNull Whether the column is not nullable. + * @param bool|null $notNull Whether the column is not nullable. * @param ForeignKeyConstraint|null $reference The foreign key constraint. * @param int|null $scale The number of digits to the right of the decimal point. * @param int|null $size The column's size. @@ -79,12 +83,11 @@ public function __construct( private string|null $comment = null, private bool $computed = false, private string|null $dbType = null, - private mixed $defaultValue = null, private array|null $enumValues = null, private string|null $extra = null, private bool $primaryKey = false, private string|null $name = null, - private bool $notNull = false, + private bool|null $notNull = null, private ForeignKeyConstraint|null $reference = null, private int|null $scale = null, private int|null $size = null, @@ -94,6 +97,11 @@ public function __construct( ) { $this->type = $type ?? static::DEFAULT_TYPE; + if (array_key_exists('defaultValue', $args)) { + $this->defaultValue = $args['defaultValue']; + unset($args['defaultValue']); + } + /** @var array $args */ foreach ($args as $property => $value) { if (property_exists($this, $property)) { @@ -159,31 +167,37 @@ public function extra(string|null $extra): static return $this; } + /** @psalm-mutation-free */ public function getCheck(): string|null { return $this->check; } + /** @psalm-mutation-free */ public function getComment(): string|null { return $this->comment; } + /** @psalm-mutation-free */ public function getDbType(): string|null { return $this->dbType; } + /** @psalm-mutation-free */ public function getDefaultValue(): mixed { - return $this->defaultValue; + return $this->defaultValue ?? null; } + /** @psalm-mutation-free */ public function getEnumValues(): array|null { return $this->enumValues; } + /** @psalm-mutation-free */ public function getExtra(): string|null { return $this->extra; @@ -191,6 +205,7 @@ public function getExtra(): string|null /** * @deprecated Will be removed in version 2.0. + * @psalm-mutation-free */ public function getName(): string|null { @@ -199,70 +214,89 @@ public function getName(): string|null /** * @deprecated Use {@see getSize()} instead. Will be removed in version 2.0. + * @psalm-mutation-free */ public function getPrecision(): int|null { return $this->getSize(); } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::MIXED; } + /** @psalm-mutation-free */ public function getReference(): ForeignKeyConstraint|null { return $this->reference; } + /** @psalm-mutation-free */ public function getScale(): int|null { return $this->scale; } + /** @psalm-mutation-free */ public function getSize(): int|null { return $this->size; } + /** @psalm-mutation-free */ public function getType(): string { return $this->type; } + /** @psalm-mutation-free */ + public function hasDefaultValue(): bool + { + return property_exists($this, 'defaultValue'); + } + /** * @deprecated Use {@see isNotNull()} instead. Will be removed in version 2.0. + * @psalm-mutation-free */ public function isAllowNull(): bool { return !$this->isNotNull(); } + /** @psalm-mutation-free */ public function isAutoIncrement(): bool { return $this->autoIncrement; } + /** @psalm-mutation-free */ public function isComputed(): bool { return $this->computed; } - public function isNotNull(): bool + /** @psalm-mutation-free */ + public function isNotNull(): bool|null { return $this->notNull; } + /** @psalm-mutation-free */ public function isPrimaryKey(): bool { return $this->primaryKey; } + /** @psalm-mutation-free */ public function isUnique(): bool { return $this->unique; } + /** @psalm-mutation-free */ public function isUnsigned(): bool { return $this->unsigned; @@ -277,12 +311,18 @@ public function name(string|null $name): static return $this; } - public function notNull(bool $notNull = true): static + public function notNull(bool|null $notNull = true): static { $this->notNull = $notNull; return $this; } + public function null(): static + { + $this->notNull = false; + return $this; + } + /** * @deprecated Use {@see size()} instead. Will be removed in version 2.0. */ diff --git a/src/Schema/Column/ArrayColumnSchema.php b/src/Schema/Column/ArrayColumnSchema.php index 7f60ea92f..0d3e24149 100644 --- a/src/Schema/Column/ArrayColumnSchema.php +++ b/src/Schema/Column/ArrayColumnSchema.php @@ -85,12 +85,14 @@ public function dimension(int $dimension): static * @return int the dimension of the array. * * @psalm-return positive-int + * @psalm-mutation-free */ public function getDimension(): int { return $this->dimension; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::ARRAY; diff --git a/src/Schema/Column/BigIntColumnSchema.php b/src/Schema/Column/BigIntColumnSchema.php index 30e0b6227..b082f2f83 100644 --- a/src/Schema/Column/BigIntColumnSchema.php +++ b/src/Schema/Column/BigIntColumnSchema.php @@ -41,6 +41,7 @@ public function dbTypecast(mixed $value): int|string|ExpressionInterface|null }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::STRING; diff --git a/src/Schema/Column/BitColumnSchema.php b/src/Schema/Column/BitColumnSchema.php index a2cdc30d7..9979afbac 100644 --- a/src/Schema/Column/BitColumnSchema.php +++ b/src/Schema/Column/BitColumnSchema.php @@ -27,6 +27,7 @@ public function dbTypecast(mixed $value): int|string|ExpressionInterface|null }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::INT; diff --git a/src/Schema/Column/BooleanColumnSchema.php b/src/Schema/Column/BooleanColumnSchema.php index 4d60ba2dd..c20e4484f 100644 --- a/src/Schema/Column/BooleanColumnSchema.php +++ b/src/Schema/Column/BooleanColumnSchema.php @@ -25,6 +25,7 @@ public function dbTypecast(mixed $value): bool|ExpressionInterface|null }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::BOOL; diff --git a/src/Schema/Column/ColumnSchemaInterface.php b/src/Schema/Column/ColumnSchemaInterface.php index ffaf24330..22843e824 100644 --- a/src/Schema/Column/ColumnSchemaInterface.php +++ b/src/Schema/Column/ColumnSchemaInterface.php @@ -148,13 +148,15 @@ public function extra(string|null $extra): static; * Returns the check constraint for the column. * * @see check() - */ + * @psalm-mutation-free + */ public function getCheck(): string|null; /** * @return string|null The comment of the column. * * @see comment() + * @psalm-mutation-free */ public function getComment(): string|null; @@ -166,6 +168,7 @@ public function getComment(): string|null; * separately via {@see getSize()}. * * @see dbType() + * @psalm-mutation-free */ public function getDbType(): string|null; @@ -173,6 +176,7 @@ public function getDbType(): string|null; * @return mixed The default value of the column. * * @see defaultValue() + * @psalm-mutation-free */ public function getDefaultValue(): mixed; @@ -180,6 +184,7 @@ public function getDefaultValue(): mixed; * @return array|null The enum values of the column. * * @see enumValues() + * @psalm-mutation-free */ public function getEnumValues(): array|null; @@ -187,6 +192,7 @@ public function getEnumValues(): array|null; * @return string|null The extra SQL for the column. * * @see extra() + * @psalm-mutation-free */ public function getExtra(): string|null; @@ -194,6 +200,7 @@ public function getExtra(): string|null; * @return string|null The name of the column. * * @deprecated Will be removed in version 2.0. + * @psalm-mutation-free */ public function getName(): string|null; @@ -203,6 +210,7 @@ public function getName(): string|null; * @see precision() * * @deprecated Use {@see getSize()} instead. Will be removed in version 2.0. + * @psalm-mutation-free */ public function getPrecision(): int|null; @@ -211,6 +219,7 @@ public function getPrecision(): int|null; * * @return string The PHP type of the column. * @psalm-return PhpType::* + * @psalm-mutation-free */ public function getPhpType(): string; @@ -218,6 +227,7 @@ public function getPhpType(): string; * Returns the reference to the foreign key constraint. * * @see reference() + * @psalm-mutation-free */ public function getReference(): ForeignKeyConstraint|null; @@ -225,6 +235,7 @@ public function getReference(): ForeignKeyConstraint|null; * @return int|null The scale of the column. * * @see scale() + * @psalm-mutation-free */ public function getScale(): int|null; @@ -232,6 +243,7 @@ public function getScale(): int|null; * @return int|null The size of the column. * * @see size() + * @psalm-mutation-free */ public function getSize(): int|null; @@ -240,15 +252,20 @@ public function getSize(): int|null; * @psalm-return ColumnType::* * * @see type() + * @psalm-mutation-free */ public function getType(): string; + /** @psalm-mutation-free */ + public function hasDefaultValue(): bool; + /** * Whether this column is nullable. * * @see allowNull() * * @deprecated Use {@see isNotNull()} instead. Will be removed in version 2.0. + * @psalm-mutation-free */ public function isAllowNull(): bool; @@ -258,6 +275,7 @@ public function isAllowNull(): bool; * This is only meaningful when {@see type} is `smallint`, `integer` or `bigint`. * * @see autoIncrement() + * @psalm-mutation-free */ public function isAutoIncrement(): bool; @@ -265,6 +283,7 @@ public function isAutoIncrement(): bool; * Whether this column is computed. * * @see computed() + * @psalm-mutation-free */ public function isComputed(): bool; @@ -272,13 +291,15 @@ public function isComputed(): bool; * Whether this column is not nullable. * * @see notNull() + * @psalm-mutation-free */ - public function isNotNull(): bool; + public function isNotNull(): bool|null; /** * Whether this column is a part of primary key. * * @see primaryKey() + * @psalm-mutation-free */ public function isPrimaryKey(): bool; @@ -286,6 +307,7 @@ public function isPrimaryKey(): bool; * Whether this column has a unique index. * * @see unique() + * @psalm-mutation-free */ public function isUnique(): bool; @@ -294,6 +316,7 @@ public function isUnique(): bool; * or `bigint`. * * @see unsigned() + * @psalm-mutation-free */ public function isUnsigned(): bool; @@ -321,6 +344,11 @@ public function name(string|null $name): static; */ public function notNull(bool $notNull = true): static; + /** + * Whether the column is nullable. Alias of {@see notNull(false)}. + */ + public function null(): static; + /** * Converts the input value according to {@see phpType} after retrieval from the database. * diff --git a/src/Schema/Column/DoubleColumnSchema.php b/src/Schema/Column/DoubleColumnSchema.php index 674e2abb0..932b5294a 100644 --- a/src/Schema/Column/DoubleColumnSchema.php +++ b/src/Schema/Column/DoubleColumnSchema.php @@ -29,6 +29,7 @@ public function dbTypecast(mixed $value): float|ExpressionInterface|null }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::FLOAT; diff --git a/src/Schema/Column/IntegerColumnSchema.php b/src/Schema/Column/IntegerColumnSchema.php index 30513ed80..e4710452a 100644 --- a/src/Schema/Column/IntegerColumnSchema.php +++ b/src/Schema/Column/IntegerColumnSchema.php @@ -29,6 +29,7 @@ public function dbTypecast(mixed $value): int|ExpressionInterface|null }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::INT; diff --git a/src/Schema/Column/StringColumnSchema.php b/src/Schema/Column/StringColumnSchema.php index 992abe7de..855bc3bd0 100644 --- a/src/Schema/Column/StringColumnSchema.php +++ b/src/Schema/Column/StringColumnSchema.php @@ -29,6 +29,7 @@ public function dbTypecast(mixed $value): mixed }; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::STRING; diff --git a/src/Schema/Column/StructuredColumnSchema.php b/src/Schema/Column/StructuredColumnSchema.php index d71315d41..be08a0b27 100644 --- a/src/Schema/Column/StructuredColumnSchema.php +++ b/src/Schema/Column/StructuredColumnSchema.php @@ -52,12 +52,14 @@ public function columns(array $columns): static * Get the metadata of the structured type columns. * * @return ColumnSchemaInterface[] + * @psalm-mutation-free */ public function getColumns(): array { return $this->columns; } + /** @psalm-mutation-free */ public function getPhpType(): string { return PhpType::ARRAY; diff --git a/tests/Db/Schema/ColumnSchemaTest.php b/tests/Db/Schema/ColumnSchemaTest.php index 3f1539653..591f1bc8c 100644 --- a/tests/Db/Schema/ColumnSchemaTest.php +++ b/tests/Db/Schema/ColumnSchemaTest.php @@ -120,12 +120,15 @@ public function testDefaultValue(): void { $column = new ColumnSchema(); + $this->assertFalse($column->hasDefaultValue()); $this->assertNull($column->getDefaultValue()); $this->assertSame($column, $column->defaultValue('test')); + $this->assertTrue($column->hasDefaultValue()); $this->assertSame('test', $column->getDefaultValue()); $column->defaultValue(null); + $this->assertTrue($column->hasDefaultValue()); $this->assertNull($column->getDefaultValue()); } @@ -180,7 +183,7 @@ public function testNotNull(): void { $column = new ColumnSchema(); - $this->assertFalse($column->isNotNull()); + $this->assertNull($column->isNotNull()); $this->assertSame($column, $column->notNull()); $this->assertTrue($column->isNotNull()); @@ -188,9 +191,13 @@ public function testNotNull(): void $this->assertFalse($column->isNotNull()); - $column->notNull(true); + $column->notNull(null); - $this->assertTrue($column->isNotNull()); + $this->assertNull($column->isNotNull()); + + $column->null(); + + $this->assertFalse($column->isNotNull()); } public function testPrecision(): void diff --git a/tests/Provider/ColumnBuilderProvider.php b/tests/Provider/ColumnBuilderProvider.php index b147df30d..ea783d70c 100644 --- a/tests/Provider/ColumnBuilderProvider.php +++ b/tests/Provider/ColumnBuilderProvider.php @@ -28,7 +28,7 @@ class ColumnBuilderProvider 'getSize' => null, 'isAutoIncrement' => false, 'isComputed' => false, - 'isNotNull' => false, + 'isNotNull' => null, 'isPrimaryKey' => false, 'isUnsigned' => false, ]; diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 63b22d580..c6161d200 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -1663,10 +1663,12 @@ public static function buildColumnDefinition(): array 'comment(null)' => ['varchar(255)', ColumnBuilder::string()->comment(null)], "defaultValue('value')" => ["varchar(255) DEFAULT 'value'", ColumnBuilder::string()->defaultValue('value')], "defaultValue('')" => ["varchar(255) DEFAULT ''", ColumnBuilder::string()->defaultValue('')], - 'defaultValue(null)' => ['varchar(255)', ColumnBuilder::string()->defaultValue(null)], + 'defaultValue(null)' => ['varchar(255) DEFAULT NULL', ColumnBuilder::string()->defaultValue(null)], 'defaultValue($expression)' => ['varchar(255) DEFAULT expression', ColumnBuilder::string()->defaultValue(new Expression('expression'))], - "integer()->defaultValue('')" => ['integer', ColumnBuilder::integer()->defaultValue('')], + 'notNull()->defaultValue(null)' => ['varchar(255) NOT NULL', ColumnBuilder::string()->notNull()->defaultValue(null)], + "integer()->defaultValue('')" => ['integer DEFAULT NULL', ColumnBuilder::integer()->defaultValue('')], 'notNull()' => ['varchar(255) NOT NULL', ColumnBuilder::string()->notNull()], + 'null()' => ['varchar(255) NULL', ColumnBuilder::string()->null()], 'integer()->primaryKey()' => ['integer PRIMARY KEY', ColumnBuilder::integer()->primaryKey()], 'size(10)' => ['varchar(10)', ColumnBuilder::string()->size(10)], 'unique()' => ['varchar(255) UNIQUE', ColumnBuilder::string()->unique()],