Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into improve-column-schema
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov committed Nov 16, 2024
2 parents f55b3b1 + 86fc92e commit f154274
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
- Enh #865: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov, @vjik)
- Enh #798: Allow `QueryInterface::one()` and `QueryInterface::all()` to return objects (@darkdef, @Tigrov)
- Enh #872: Use `#[\SensitiveParameter]` attribute to mark sensitive parameters (@heap-s)
- New #864, #897: Realize column factory (@Tigrov)
- New #864, #897, #898: Realize column factory (@Tigrov)
- Enh #875: Ignore "Packets out of order..." warnings in `AbstractPdoCommand::internalExecute()` method (@Tigrov)
- Enh #877: Separate column type constants (@Tigrov)
- Enh #878: Realize `ColumnBuilder` class (@Tigrov)
Expand Down
74 changes: 72 additions & 2 deletions src/Schema/Column/AbstractColumnFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@

use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Constant\PseudoType;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Syntax\ColumnDefinitionParser;

use function array_diff_key;
use function is_numeric;
use function preg_match;
use function str_replace;
use function substr;

use const PHP_INT_SIZE;

/**
Expand Down Expand Up @@ -89,12 +96,22 @@ public function fromType(string $type, array $info = []): ColumnSchemaInterface
unset($info['type']);

if ($type === ColumnType::ARRAY && empty($info['column']) && !empty($info['dbType'])) {
$info['column'] = $this->fromDbType($info['dbType'], $info);
/** @psalm-suppress ArgumentTypeCoercion */
$info['column'] = $this->fromDbType(
$info['dbType'],
array_diff_key($info, ['dimension' => 1, 'defaultValueRaw' => 1])
);
}

$columnClass = $this->getColumnClass($type, $info);

return new $columnClass($type, ...$info);
$column = new $columnClass($type, ...$info);

if (isset($info['defaultValueRaw'])) {
$column->defaultValue($this->normalizeDefaultValue($info['defaultValueRaw'], $column));
}

return $column;
}

/**
Expand Down Expand Up @@ -210,4 +227,57 @@ protected function isType(string $type): bool
default => false,
};
}

/**
* Converts column's default value according to {@see ColumnSchemaInterface::getPhpType()} after retrieval from the
* database.
*
* @param string|null $defaultValue The default value retrieved from the database.
* @param ColumnSchemaInterface $column The column schema object.
*
* @return mixed The normalized default value.
*/
protected function normalizeDefaultValue(string|null $defaultValue, ColumnSchemaInterface $column): mixed
{
if (
$defaultValue === null
|| $defaultValue === ''
|| $column->isPrimaryKey()
|| $column->isComputed()
|| preg_match('/^\(?NULL\b/i', $defaultValue) === 1
) {
return null;
}

return $this->normalizeNotNullDefaultValue($defaultValue, $column);
}

/**
* Converts a not null default value according to {@see ColumnSchemaInterface::getPhpType()}.
*/
protected function normalizeNotNullDefaultValue(string $defaultValue, ColumnSchemaInterface $column): mixed
{
$value = $defaultValue;

if ($value[0] === '(' && $value[-1] === ')') {
$value = substr($value, 1, -1);
}

if (is_numeric($value)) {
return $column->phpTypecast($value);
}

if ($value[0] === "'" && $value[-1] === "'") {
$value = substr($value, 1, -1);
$value = str_replace("''", "'", $value);

return $column->phpTypecast($value);
}

return match ($value) {
'true' => true,
'false' => false,
default => new Expression($defaultValue),
};
}
}
1 change: 1 addition & 0 deletions src/Schema/Column/ColumnFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* computed?: bool,
* dbType?: string|null,
* defaultValue?: mixed,
* defaultValueRaw?: string|null,
* dimension?: positive-int,
* enumValues?: array|null,
* extra?: string|null,
Expand Down
46 changes: 40 additions & 6 deletions tests/AbstractColumnFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@

namespace Yiisoft\Db\Tests;

use PHPUnit\Framework\Attributes\DataProviderExternal;
use PHPUnit\Framework\TestCase;
use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Schema\Column\StringColumnSchema;
use Yiisoft\Db\Tests\Provider\ColumnBuilderProvider;
use Yiisoft\Db\Tests\Provider\ColumnFactoryProvider;
use Yiisoft\Db\Tests\Support\TestTrait;

abstract class AbstractColumnFactoryTest extends TestCase
{
use TestTrait;

/** @dataProvider \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider::types */
#[DataProviderExternal(ColumnFactoryProvider::class, 'types')]
public function testFromDbType(string $dbType, string $expectedType, string $expectedInstanceOf): void
{
$db = $this->getConnection();
Expand All @@ -28,9 +31,7 @@ public function testFromDbType(string $dbType, string $expectedType, string $exp
$db->close();
}

/**
* @dataProvider \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider::definitions
*/
#[DataProviderExternal(ColumnFactoryProvider::class, 'definitions')]
public function testFromDefinition(
string $definition,
string $expectedType,
Expand All @@ -57,7 +58,7 @@ public function testFromDefinition(
$db->close();
}

/** @dataProvider \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider::pseudoTypes */
#[DataProviderExternal(ColumnFactoryProvider::class, 'pseudoTypes')]
public function testFromPseudoType(
string $pseudoType,
string $expectedType,
Expand All @@ -84,7 +85,7 @@ public function testFromPseudoType(
$db->close();
}

/** @dataProvider \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider::types */
#[DataProviderExternal(ColumnFactoryProvider::class, 'types')]
public function testFromType(string $type, string $expectedType, string $expectedInstanceOf): void
{
$db = $this->getConnection();
Expand Down Expand Up @@ -112,4 +113,37 @@ public function testFromDefinitionWithExtra(): void

$db->close();
}

#[DataProviderExternal(ColumnFactoryProvider::class, 'defaultValueRaw')]
public function testFromTypeDefaultValueRaw(string $type, string|null $defaultValueRaw, mixed $expected): void
{
$db = $this->getConnection();
$columnFactory = $db->getSchema()->getColumnFactory();

$column = $columnFactory->fromType($type, ['defaultValueRaw' => $defaultValueRaw]);

if (is_scalar($expected)) {
$this->assertSame($expected, $column->getDefaultValue());
} else {
$this->assertEquals($expected, $column->getDefaultValue());
}

$db->close();
}

public function testNullDefaultValueRaw(): void
{
$db = $this->getConnection();
$columnFactory = $db->getSchema()->getColumnFactory();

$column = $columnFactory->fromType(ColumnType::INTEGER, ['defaultValueRaw' => '1', 'primaryKey' => true]);

$this->assertNull($column->getDefaultValue());

$column = $columnFactory->fromType(ColumnType::INTEGER, ['defaultValueRaw' => '1', 'computed' => true]);

$this->assertNull($column->getDefaultValue());

$db->close();
}
}
30 changes: 30 additions & 0 deletions tests/Provider/ColumnFactoryProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Constant\PseudoType;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Schema\Column\BigIntColumnSchema;
use Yiisoft\Db\Schema\Column\BinaryColumnSchema;
use Yiisoft\Db\Schema\Column\BooleanColumnSchema;
Expand Down Expand Up @@ -69,4 +70,33 @@ public static function types(): array
'json' => [ColumnType::JSON, ColumnType::JSON, JsonColumnSchema::class],
];
}

public static function defaultValueRaw(): array
{
return [
// type, default value, expected value
'null' => [ColumnType::STRING, null, null],
'(null)' => [ColumnType::STRING, '(null)', null],
'NULL' => [ColumnType::STRING, 'NULL', null],
'(NULL)' => [ColumnType::STRING, '(NULL)', null],
'' => [ColumnType::STRING, '', null],
'(0)' => [ColumnType::INTEGER, '(0)', 0],
'-1' => [ColumnType::INTEGER, '-1', -1],
'(-1)' => [ColumnType::INTEGER, '(-1)', -1],
'0.0' => [ColumnType::DOUBLE, '0.0', 0.0],
'(0.0)' => [ColumnType::DOUBLE, '(0.0)', 0.0],
'-1.1' => [ColumnType::DOUBLE, '-1.1', -1.1],
'(-1.1)' => [ColumnType::DOUBLE, '(-1.1)', -1.1],
'true' => [ColumnType::BOOLEAN, 'true', true],
'false' => [ColumnType::BOOLEAN, 'false', false],
'1' => [ColumnType::BOOLEAN, '1', true],
'0' => [ColumnType::BOOLEAN, '0', false],
"''" => [ColumnType::STRING, "''", ''],
"('')" => [ColumnType::STRING, "('')", ''],
"'str''ing'" => [ColumnType::STRING, "'str''ing'", "str'ing"],
"('str''ing')" => [ColumnType::STRING, "('str''ing')", "str'ing"],
'CURRENT_TIMESTAMP' => [ColumnType::TIMESTAMP, 'CURRENT_TIMESTAMP', new Expression('CURRENT_TIMESTAMP')],
'(now())' => [ColumnType::TIMESTAMP, '(now())', new Expression('(now())')],
];
}
}

0 comments on commit f154274

Please sign in to comment.