From 75004227b05acd49a5951d8d6d9b5c200f1d3c5d Mon Sep 17 00:00:00 2001 From: Tigrov Date: Tue, 13 Aug 2024 16:27:38 +0700 Subject: [PATCH 1/4] Realize column factory --- src/Column/ColumnFactory.php | 62 +++++++++++++++++ src/Schema.php | 87 +++--------------------- tests/ColumnFactoryTest.php | 34 +++++++++ tests/Provider/ColumnFactoryProvider.php | 62 +++++++++++++++++ tests/SchemaTest.php | 9 +++ 5 files changed, 176 insertions(+), 78 deletions(-) create mode 100644 src/Column/ColumnFactory.php create mode 100644 tests/ColumnFactoryTest.php create mode 100644 tests/Provider/ColumnFactoryProvider.php diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php new file mode 100644 index 00000000..8fdc81f7 --- /dev/null +++ b/src/Column/ColumnFactory.php @@ -0,0 +1,62 @@ + SchemaInterface::TYPE_BOOLEAN, + 'boolean' => SchemaInterface::TYPE_BOOLEAN, + 'bit' => SchemaInterface::TYPE_BIT, + 'tinyint' => SchemaInterface::TYPE_TINYINT, + 'smallint' => SchemaInterface::TYPE_SMALLINT, + 'mediumint' => SchemaInterface::TYPE_INTEGER, + 'int' => SchemaInterface::TYPE_INTEGER, + 'integer' => SchemaInterface::TYPE_INTEGER, + 'bigint' => SchemaInterface::TYPE_BIGINT, + 'float' => SchemaInterface::TYPE_FLOAT, + 'real' => SchemaInterface::TYPE_FLOAT, + 'double' => SchemaInterface::TYPE_DOUBLE, + 'decimal' => SchemaInterface::TYPE_DECIMAL, + 'numeric' => SchemaInterface::TYPE_DECIMAL, + 'char' => SchemaInterface::TYPE_CHAR, + 'varchar' => SchemaInterface::TYPE_STRING, + 'string' => SchemaInterface::TYPE_STRING, + 'enum' => SchemaInterface::TYPE_STRING, + 'tinytext' => SchemaInterface::TYPE_TEXT, + 'mediumtext' => SchemaInterface::TYPE_TEXT, + 'longtext' => SchemaInterface::TYPE_TEXT, + 'text' => SchemaInterface::TYPE_TEXT, + 'blob' => SchemaInterface::TYPE_BINARY, + 'year' => SchemaInterface::TYPE_DATE, + 'date' => SchemaInterface::TYPE_DATE, + 'time' => SchemaInterface::TYPE_TIME, + 'datetime' => SchemaInterface::TYPE_DATETIME, + 'timestamp' => SchemaInterface::TYPE_TIMESTAMP, + 'json' => SchemaInterface::TYPE_JSON, + ]; + + protected function getType(string $dbType, array $info = []): string + { + $type = self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING; + + if ( + ($type === SchemaInterface::TYPE_BIT || $type === SchemaInterface::TYPE_TINYINT) + && isset($info['size']) + && $info['size'] === 1 + ) { + return SchemaInterface::TYPE_BOOLEAN; + } + + return $type; + } +} diff --git a/src/Schema.php b/src/Schema.php index 9cf5dd9b..fa175333 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -17,16 +17,16 @@ use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Helper\DbArrayHelper; use Yiisoft\Db\Schema\Builder\ColumnInterface; +use Yiisoft\Db\Schema\Column\ColumnFactoryInterface; use Yiisoft\Db\Schema\Column\ColumnSchemaInterface; use Yiisoft\Db\Schema\TableSchemaInterface; +use Yiisoft\Db\Sqlite\Column\ColumnFactory; use function array_column; use function array_map; use function array_merge; use function count; -use function explode; use function md5; -use function preg_match; use function preg_replace; use function serialize; use function strncasecmp; @@ -75,48 +75,16 @@ */ final class Schema extends AbstractPdoSchema { - /** - * Mapping from physical column types (keys) to abstract column types (values). - * - * @var string[] - */ - private const TYPE_MAP = [ - 'tinyint' => self::TYPE_TINYINT, - 'bit' => self::TYPE_BIT, - 'boolean' => self::TYPE_BOOLEAN, - 'bool' => self::TYPE_BOOLEAN, - 'smallint' => self::TYPE_SMALLINT, - 'mediumint' => self::TYPE_INTEGER, - 'int' => self::TYPE_INTEGER, - 'integer' => self::TYPE_INTEGER, - 'bigint' => self::TYPE_BIGINT, - 'float' => self::TYPE_FLOAT, - 'double' => self::TYPE_DOUBLE, - 'real' => self::TYPE_FLOAT, - 'decimal' => self::TYPE_DECIMAL, - 'numeric' => self::TYPE_DECIMAL, - 'tinytext' => self::TYPE_TEXT, - 'mediumtext' => self::TYPE_TEXT, - 'longtext' => self::TYPE_TEXT, - 'text' => self::TYPE_TEXT, - 'varchar' => self::TYPE_STRING, - 'string' => self::TYPE_STRING, - 'char' => self::TYPE_CHAR, - 'blob' => self::TYPE_BINARY, - 'datetime' => self::TYPE_DATETIME, - 'year' => self::TYPE_DATE, - 'date' => self::TYPE_DATE, - 'time' => self::TYPE_TIME, - 'timestamp' => self::TYPE_TIMESTAMP, - 'enum' => self::TYPE_STRING, - 'json' => self::TYPE_JSON, - ]; - public function createColumn(string $type, array|int|string $length = null): ColumnInterface { return new Column($type, $length); } + public function getColumnFactory(): ColumnFactoryInterface + { + return new ColumnFactory(); + } + /** * Returns all table names in the database. * @@ -477,52 +445,15 @@ public function getSchemaDefaultValues(string $schema = '', bool $refresh = fals private function loadColumnSchema(array $info): ColumnSchemaInterface { $dbType = strtolower($info['type']); - $type = $this->getColumnType($dbType, $info); - $isUnsigned = str_contains($dbType, 'unsigned'); - /** @psalm-var ColumnInfo $info */ - $column = $this->createColumnSchema($type, unsigned: $isUnsigned); - $column->name($info['name']); - $column->size($info['size'] ?? null); - $column->precision($info['precision'] ?? null); - $column->scale($info['scale'] ?? null); + $column = $this->getColumnFactory()->fromDefinition($dbType, ['name' => $info['name']]); + $column->dbType($dbType); $column->allowNull(!$info['notnull']); $column->primaryKey((bool) $info['pk']); - $column->dbType($dbType); $column->defaultValue($this->normalizeDefaultValue($info['dflt_value'], $column)); return $column; } - /** - * Get the abstract data type for the database data type. - * - * @param string $dbType The database data type - * @param array $info Column information. - * - * @return string The abstract data type. - */ - private function getColumnType(string $dbType, array &$info): string - { - preg_match('/^(\w*)(?:\(([^)]+)\))?/', $dbType, $matches); - $dbType = strtolower($matches[1]); - - if (!empty($matches[2])) { - $values = explode(',', $matches[2], 2); - $info['size'] = (int) $values[0]; - $info['precision'] = (int) $values[0]; - - if (isset($values[1])) { - $info['scale'] = (int) $values[1]; - } - - if (($dbType === 'tinyint' || $dbType === 'bit') && $info['size'] === 1) { - return self::TYPE_BOOLEAN; - } - } - - return self::TYPE_MAP[$dbType] ?? self::TYPE_STRING; - } - /** * Converts column's default value according to {@see ColumnSchema::phpType} after retrieval from the database. * diff --git a/tests/ColumnFactoryTest.php b/tests/ColumnFactoryTest.php new file mode 100644 index 00000000..a9b02608 --- /dev/null +++ b/tests/ColumnFactoryTest.php @@ -0,0 +1,34 @@ + 1]]; + $definitions[] = ['tinyint(1)', 'boolean', BooleanColumnSchema::class, ['getSize' => 1]]; + + return $definitions; + } +} diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 2a171127..758383d2 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -11,6 +11,7 @@ use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidConfigException; use Yiisoft\Db\Exception\NotSupportedException; +use Yiisoft\Db\Sqlite\Column\ColumnFactory; use Yiisoft\Db\Sqlite\Schema; use Yiisoft\Db\Sqlite\Tests\Support\TestTrait; use Yiisoft\Db\Tests\Common\CommonSchemaTest; @@ -359,4 +360,12 @@ public function testNotConnectionPDO(): void $schema->refresh(); } + + public function testGetColumnFactory(): void + { + $db = $this->getConnection(); + $factory = $db->getSchema()->getColumnFactory(); + + $this->assertInstanceOf(ColumnFactory::class, $factory); + } } From 24895e1f110711a3e61b89f7c5869e33a5851e56 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 14 Aug 2024 10:59:31 +0700 Subject: [PATCH 2/4] Fix psalm issue --- src/Column/ColumnFactory.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php index 8fdc81f7..5eff500e 100644 --- a/src/Column/ColumnFactory.php +++ b/src/Column/ColumnFactory.php @@ -12,6 +12,8 @@ class ColumnFactory extends \Yiisoft\Db\Schema\Column\ColumnFactory * Mapping from physical column types (keys) to abstract column types (values). * * @var string[] + * + * @psalm-suppress MissingClassConstType */ private const TYPE_MAP = [ 'bool' => SchemaInterface::TYPE_BOOLEAN, From 0859156f914974be5ca8ab9929a788a4c9710116 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Tue, 27 Aug 2024 18:56:33 +0700 Subject: [PATCH 3/4] Change `ColumnFactory` to `AbstractColumnFactory` --- CHANGELOG.md | 1 + src/Column/ColumnFactory.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 517db4d4..8e5666c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Enh #310: Add JSON overlaps condition builder (@Tigrov) - Enh #312: Update `bit` type according to main PR yiisoft/db#860 (@Tigrov) - Enh #315: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov) +- Enh #314: Implement `ColumnFactory` class (@Tigrov) ## 1.2.0 March 21, 2024 diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php index 5eff500e..8f31586c 100644 --- a/src/Column/ColumnFactory.php +++ b/src/Column/ColumnFactory.php @@ -4,9 +4,10 @@ namespace Yiisoft\Db\Sqlite\Column; +use Yiisoft\Db\Schema\Column\AbstractColumnFactory; use Yiisoft\Db\Schema\SchemaInterface; -class ColumnFactory extends \Yiisoft\Db\Schema\Column\ColumnFactory +class ColumnFactory extends AbstractColumnFactory { /** * Mapping from physical column types (keys) to abstract column types (values). From 961dd9aa12a6fdf3a3ab82a5340ae4b5d513f511 Mon Sep 17 00:00:00 2001 From: Sergei Tigrov Date: Sun, 1 Sep 2024 14:35:02 +0700 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Sergei Predvoditelev --- src/Column/ColumnFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php index 8f31586c..7b759d7b 100644 --- a/src/Column/ColumnFactory.php +++ b/src/Column/ColumnFactory.php @@ -7,7 +7,7 @@ use Yiisoft\Db\Schema\Column\AbstractColumnFactory; use Yiisoft\Db\Schema\SchemaInterface; -class ColumnFactory extends AbstractColumnFactory +final class ColumnFactory extends AbstractColumnFactory { /** * Mapping from physical column types (keys) to abstract column types (values).