diff --git a/CHANGELOG.md b/CHANGELOG.md index acae62be..8c6e305e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Enh #312: Refactor `bit` type (@Tigrov) - Enh #315: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov) - Enh #317: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov) +- Enh #316: 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..dee9e8a5 --- /dev/null +++ b/src/Column/ColumnFactory.php @@ -0,0 +1,82 @@ + SchemaInterface::TYPE_BOOLEAN, + 'tinyint' => SchemaInterface::TYPE_TINYINT, + 'smallint' => SchemaInterface::TYPE_SMALLINT, + 'int' => SchemaInterface::TYPE_INTEGER, + 'bigint' => SchemaInterface::TYPE_BIGINT, + 'numeric' => SchemaInterface::TYPE_DECIMAL, + 'decimal' => SchemaInterface::TYPE_DECIMAL, + 'smallmoney' => SchemaInterface::TYPE_MONEY, + 'money' => SchemaInterface::TYPE_MONEY, + + /** Approximate numbers */ + 'float' => SchemaInterface::TYPE_FLOAT, + 'real' => SchemaInterface::TYPE_FLOAT, + 'double' => SchemaInterface::TYPE_DOUBLE, + + /** Date and time */ + 'date' => SchemaInterface::TYPE_DATE, + 'time' => SchemaInterface::TYPE_TIME, + 'smalldatetime' => SchemaInterface::TYPE_DATETIME, + 'datetime' => SchemaInterface::TYPE_DATETIME, + 'datetime2' => SchemaInterface::TYPE_DATETIME, + 'datetimeoffset' => SchemaInterface::TYPE_DATETIME, + + /** Character strings */ + 'char' => SchemaInterface::TYPE_CHAR, + 'varchar' => SchemaInterface::TYPE_STRING, + 'text' => SchemaInterface::TYPE_TEXT, + + /** Unicode character strings */ + 'nchar' => SchemaInterface::TYPE_CHAR, + 'nvarchar' => SchemaInterface::TYPE_STRING, + 'ntext' => SchemaInterface::TYPE_TEXT, + + /** Binary strings */ + 'binary' => SchemaInterface::TYPE_BINARY, + 'varbinary' => SchemaInterface::TYPE_BINARY, + 'image' => SchemaInterface::TYPE_BINARY, + + /** + * Other data types 'cursor' type can't be used with tables + */ + 'timestamp' => SchemaInterface::TYPE_TIMESTAMP, + 'hierarchyid' => SchemaInterface::TYPE_STRING, + 'uniqueidentifier' => SchemaInterface::TYPE_STRING, + 'sql_variant' => SchemaInterface::TYPE_STRING, + 'xml' => SchemaInterface::TYPE_STRING, + 'table' => SchemaInterface::TYPE_STRING, + ]; + + protected function getType(string $dbType, array $info = []): string + { + return self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING; + } + + public function fromType(string $type, array $info = []): ColumnSchemaInterface + { + if ($type === SchemaInterface::TYPE_BINARY) { + return (new BinaryColumnSchema($type))->load($info); + } + + return parent::fromType($type, $info); + } +} diff --git a/src/Schema.php b/src/Schema.php index c7cfc9d2..4bb0598c 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -14,21 +14,20 @@ use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidConfigException; use Yiisoft\Db\Helper\DbArrayHelper; -use Yiisoft\Db\Mssql\Column\BinaryColumnSchema; +use Yiisoft\Db\Mssql\Column\ColumnFactory; use Yiisoft\Db\Schema\Builder\ColumnInterface; +use Yiisoft\Db\Schema\Column\ColumnFactoryInterface; use Yiisoft\Db\Schema\Column\ColumnSchemaInterface; use Yiisoft\Db\Schema\TableSchemaInterface; use function array_change_key_case; use function array_map; -use function explode; use function is_array; use function md5; use function preg_match; use function serialize; use function str_replace; use function strcasecmp; -use function stripos; /** * Implements the MSSQL Server specific schema, supporting MSSQL Server 2017 and above. @@ -68,67 +67,16 @@ final class Schema extends AbstractPdoSchema */ protected string|null $defaultSchema = 'dbo'; - /** - * Mapping from physical column types (keys) to abstract column types (values). - * - * @var string[] - */ - private const TYPE_MAP = [ - /** Exact numbers */ - 'bigint' => self::TYPE_BIGINT, - 'numeric' => self::TYPE_DECIMAL, - 'bit' => self::TYPE_BOOLEAN, - 'smallint' => self::TYPE_SMALLINT, - 'decimal' => self::TYPE_DECIMAL, - 'smallmoney' => self::TYPE_MONEY, - 'int' => self::TYPE_INTEGER, - 'tinyint' => self::TYPE_TINYINT, - 'money' => self::TYPE_MONEY, - - /** Approximate numbers */ - 'float' => self::TYPE_FLOAT, - 'double' => self::TYPE_DOUBLE, - 'real' => self::TYPE_FLOAT, - - /** Date and time */ - 'date' => self::TYPE_DATE, - 'datetimeoffset' => self::TYPE_DATETIME, - 'datetime2' => self::TYPE_DATETIME, - 'smalldatetime' => self::TYPE_DATETIME, - 'datetime' => self::TYPE_DATETIME, - 'time' => self::TYPE_TIME, - - /** Character strings */ - 'char' => self::TYPE_CHAR, - 'varchar' => self::TYPE_STRING, - 'text' => self::TYPE_TEXT, - - /** Unicode character strings */ - 'nchar' => self::TYPE_CHAR, - 'nvarchar' => self::TYPE_STRING, - 'ntext' => self::TYPE_TEXT, - - /** Binary strings */ - 'binary' => self::TYPE_BINARY, - 'varbinary' => self::TYPE_BINARY, - 'image' => self::TYPE_BINARY, - - /** - * Other data types 'cursor' type can't be used with tables - */ - 'timestamp' => self::TYPE_TIMESTAMP, - 'hierarchyid' => self::TYPE_STRING, - 'uniqueidentifier' => self::TYPE_STRING, - 'sql_variant' => self::TYPE_STRING, - 'xml' => self::TYPE_STRING, - 'table' => self::TYPE_STRING, - ]; - public function createColumn(string $type, array|int|string|null $length = null): ColumnInterface { return new Column($type, $length); } + public function getColumnFactory(): ColumnFactoryInterface + { + return new ColumnFactory(); + } + /** * Resolves the table name and schema name (if any). * @@ -419,14 +367,9 @@ protected function loadTableDefaultValues(string $tableName): array private function loadColumnSchema(array $info): ColumnSchemaInterface { $dbType = $info['data_type']; - $type = $this->getColumnType($dbType, $info); - $isUnsigned = stripos($dbType, 'unsigned') !== false; /** @psalm-var ColumnArray $info */ - $column = $this->createColumnSchema($type, unsigned: $isUnsigned); + $column = $this->getColumnFactory()->fromDefinition($dbType); $column->name($info['column_name']); - $column->size($info['size'] ?? null); - $column->precision($info['precision'] ?? null); - $column->scale($info['scale'] ?? null); $column->allowNull($info['is_nullable'] === 'YES'); $column->dbType($dbType); $column->enumValues([]); // MSSQL has only vague equivalents to enum. @@ -439,41 +382,6 @@ private function loadColumnSchema(array $info): ColumnSchemaInterface 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]; - } - } - - return self::TYPE_MAP[$dbType] ?? self::TYPE_STRING; - } - - protected function createColumnSchemaFromType(string $type, bool $isUnsigned = false): ColumnSchemaInterface - { - if ($type === self::TYPE_BINARY) { - return new BinaryColumnSchema($type); - } - - return parent::createColumnSchemaFromType($type, $isUnsigned); - } - /** * 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..21150e9c --- /dev/null +++ b/tests/ColumnFactoryTest.php @@ -0,0 +1,34 @@ +close(); } + + public function testGetColumnFactory(): void + { + $db = $this->getConnection(); + $factory = $db->getSchema()->getColumnFactory(); + + $this->assertInstanceOf(ColumnFactory::class, $factory); + } }