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 new file mode 100644 index 00000000..7b759d7b --- /dev/null +++ b/src/Column/ColumnFactory.php @@ -0,0 +1,65 @@ + 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 9fd5c674..0252be1c 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_change_key_case; use function array_column; use function array_map; 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); + } }