diff --git a/CHANGELOG.md b/CHANGELOG.md index 629f90f..d521dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Chg #272: Replace call of `SchemaInterface::getRawTableName()` to `QuoterInterface::getRawTableName()` (@Tigrov) - Enh #275: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov) - Enh #277: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov) +- Enh #276: Implement `ColumnFactory` class (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php new file mode 100644 index 0000000..3adc1b2 --- /dev/null +++ b/src/Column/ColumnFactory.php @@ -0,0 +1,81 @@ + SchemaInterface::TYPE_CHAR, + 'nchar' => SchemaInterface::TYPE_CHAR, + 'varchar2' => SchemaInterface::TYPE_STRING, + 'nvarchar2' => SchemaInterface::TYPE_STRING, + 'clob' => SchemaInterface::TYPE_TEXT, + 'nclob' => SchemaInterface::TYPE_TEXT, + 'blob' => SchemaInterface::TYPE_BINARY, + 'bfile' => SchemaInterface::TYPE_BINARY, + 'long raw' => SchemaInterface::TYPE_BINARY, + 'raw' => SchemaInterface::TYPE_BINARY, + 'number' => SchemaInterface::TYPE_DECIMAL, + 'binary_float' => SchemaInterface::TYPE_FLOAT, // 32 bit + 'binary_double' => SchemaInterface::TYPE_DOUBLE, // 64 bit + 'float' => SchemaInterface::TYPE_DOUBLE, // 126 bit + 'date' => SchemaInterface::TYPE_DATE, + 'interval day to second' => SchemaInterface::TYPE_TIME, + 'timestamp' => SchemaInterface::TYPE_TIMESTAMP, + 'timestamp with time zone' => SchemaInterface::TYPE_TIMESTAMP, + 'timestamp with local time zone' => SchemaInterface::TYPE_TIMESTAMP, + + /** Deprecated */ + 'long' => SchemaInterface::TYPE_TEXT, + ]; + + protected function getType(string $dbType, array $info = []): string + { + $dbType = strtolower($dbType); + + if ($dbType === 'number') { + $scale = isset($info['scale']) ? (int) $info['scale'] : null; + + return match ($scale) { + null => SchemaInterface::TYPE_DOUBLE, + 0 => SchemaInterface::TYPE_INTEGER, + default => SchemaInterface::TYPE_DECIMAL, + }; + } + + $dbType = preg_replace('/\([^)]+\)/', '', $dbType); + + if ($dbType === 'interval day to second' && isset($info['precision']) && $info['precision'] > 0) { + return SchemaInterface::TYPE_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 b7b5a97..a5d76d0 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -17,8 +17,9 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Helper\DbArrayHelper; -use Yiisoft\Db\Oracle\Column\BinaryColumnSchema; +use Yiisoft\Db\Oracle\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; @@ -29,10 +30,8 @@ use function is_array; use function md5; use function preg_match; -use function preg_replace; use function serialize; use function str_replace; -use function strtolower; use function trim; /** @@ -68,38 +67,6 @@ */ final class Schema extends AbstractPdoSchema { - /** - * The mapping from physical column types (keys) to abstract column types (values). - * - * @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html - * - * @var string[] - */ - private const TYPE_MAP = [ - 'char' => self::TYPE_CHAR, - 'nchar' => self::TYPE_CHAR, - 'varchar2' => self::TYPE_STRING, - 'nvarchar2' => self::TYPE_STRING, - 'clob' => self::TYPE_TEXT, - 'nclob' => self::TYPE_TEXT, - 'blob' => self::TYPE_BINARY, - 'bfile' => self::TYPE_BINARY, - 'long raw' => self::TYPE_BINARY, - 'raw' => self::TYPE_BINARY, - 'number' => self::TYPE_DECIMAL, - 'binary_float' => self::TYPE_FLOAT, // 32 bit - 'binary_double' => self::TYPE_DOUBLE, // 64 bit - 'float' => self::TYPE_DOUBLE, // 126 bit - 'timestamp' => self::TYPE_TIMESTAMP, - 'timestamp with time zone' => self::TYPE_TIMESTAMP, - 'timestamp with local time zone' => self::TYPE_TIMESTAMP, - 'date' => self::TYPE_DATE, - 'interval day to second' => self::TYPE_TIME, - - /** Deprecated */ - 'long' => self::TYPE_TEXT, - ]; - public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema) { $this->defaultSchema = $defaultSchema; @@ -111,6 +78,11 @@ public function createColumn(string $type, array|int|string $length = null): Col return new Column($type, $length); } + public function getColumnFactory(): ColumnFactoryInterface + { + return new ColumnFactory(); + } + protected function resolveTableName(string $name): TableSchemaInterface { $resolvedName = new TableSchema(); @@ -459,32 +431,22 @@ protected function getTableSequenceName(string $tableName): string|null private function loadColumnSchema(array $info): ColumnSchemaInterface { $dbType = $info['data_type']; - $type = $this->extractColumnType($dbType, $info); - - $column = $this->createColumnSchema($type); + $column = $this->getColumnFactory()->fromDbType($dbType, [ + 'scale' => $info['data_scale'], + 'precision' => $info['data_precision'], + ]); $column->name($info['column_name']); $column->allowNull($info['nullable'] === 'Y'); $column->comment($info['column_comment']); $column->primaryKey((bool) $info['is_pk']); $column->autoIncrement($info['identity_column'] === 'YES'); $column->size((int) $info['data_length']); - $column->precision($info['data_precision'] !== null ? (int) $info['data_precision'] : null); - $column->scale($info['data_scale'] !== null ? (int) $info['data_scale'] : null); $column->dbType($dbType); $column->defaultValue($this->normalizeDefaultValue($info['data_default'], $column)); return $column; } - 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. * @@ -657,38 +619,6 @@ public function findUniqueIndexes(TableSchemaInterface $table): array return $result; } - /** - * Extracts the data type for the given column. - * - * @param string $dbType The database data type - * @param array $info Column information. - * @psalm-param ColumnInfoArray $info - * - * @return string The abstract column type. - */ - private function extractColumnType(string $dbType, array $info): string - { - $dbType = strtolower($dbType); - - if ($dbType === 'number') { - $scale = $info['data_scale'] !== null ? (int) $info['data_scale'] : null; - - return match ($scale) { - null => self::TYPE_DOUBLE, - 0 => self::TYPE_INTEGER, - default => self::TYPE_DECIMAL, - }; - } - - $dbType = preg_replace('/\([^)]+\)/', '', $dbType); - - if ($dbType === 'interval day to second' && $info['data_precision'] > 0) { - return self::TYPE_STRING; - } - - return self::TYPE_MAP[$dbType] ?? self::TYPE_STRING; - } - /** * Loads multiple types of constraints and returns the specified ones. * diff --git a/tests/ColumnFactoryTest.php b/tests/ColumnFactoryTest.php new file mode 100644 index 0000000..390ce1c --- /dev/null +++ b/tests/ColumnFactoryTest.php @@ -0,0 +1,34 @@ +refresh(); } + + public function testGetColumnFactory(): void + { + $db = $this->getConnection(); + $factory = $db->getSchema()->getColumnFactory(); + + $this->assertInstanceOf(ColumnFactory::class, $factory); + } }