From 6758b75551b308ba1e8e3344e3b77802acfa6bac Mon Sep 17 00:00:00 2001 From: Sergei Tigrov Date: Sun, 1 Sep 2024 14:44:00 +0700 Subject: [PATCH] Realize column factory (#346) --- CHANGELOG.md | 1 + src/Column/ColumnFactory.php | 76 ++++++++++++++++++++ src/Schema.php | 90 +++--------------------- tests/ColumnFactoryTest.php | 34 +++++++++ tests/Provider/ColumnFactoryProvider.php | 61 ++++++++++++++++ tests/SchemaTest.php | 9 +++ 6 files changed, 190 insertions(+), 81 deletions(-) create mode 100644 src/Column/ColumnFactory.php create mode 100644 tests/ColumnFactoryTest.php create mode 100644 tests/Provider/ColumnFactoryProvider.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ef93dfc0..cfed07be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Chg #339: Replace call of `SchemaInterface::getRawTableName()` to `QuoterInterface::getRawTableName()` (@Tigrov) - Enh #342: Add JSON overlaps condition builder (@Tigrov) - Enh #344: Update `bit` type according to main PR yiisoft/db#860 (@Tigrov) +- Enh #346: Implement `ColumnFactory` class (@Tigrov) - Enh #347, #353: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov) - Bug #349, #352: Restore connection if closed by connection timeout (@Tigrov) diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php new file mode 100644 index 00000000..3b902d5f --- /dev/null +++ b/src/Column/ColumnFactory.php @@ -0,0 +1,76 @@ + 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, + 'varbinary' => SchemaInterface::TYPE_BINARY, + 'blob' => SchemaInterface::TYPE_BINARY, + 'longblob' => 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, + ]; + + public function fromDefinition(string $definition, array $info = []): ColumnSchemaInterface + { + if (str_starts_with($definition, 'enum(')) { + preg_match('/^enum\(([^)]+)\)\s*/', $definition, $matches); + preg_match_all("/'([^']*)'/", $matches[1], $values); + + $info['enum_values'] = $values[1]; + + return $this->fromDbType('enum', $info); + } + + return parent::fromDefinition($definition, $info); + } + + protected function getType(string $dbType, array $info = []): string + { + $type = self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING; + + if ($type === SchemaInterface::TYPE_BIT && isset($info['size']) && $info['size'] === 1) { + return SchemaInterface::TYPE_BOOLEAN; + } + + return $type; + } +} diff --git a/src/Schema.php b/src/Schema.php index 5f0fce64..d4d461d0 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -14,7 +14,9 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Helper\DbArrayHelper; +use Yiisoft\Db\Mysql\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; @@ -22,7 +24,6 @@ use function array_map; use function array_values; use function bindec; -use function explode; use function in_array; use function is_string; use function ksort; @@ -76,48 +77,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, - '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, - 'longblob' => self::TYPE_BINARY, - 'blob' => self::TYPE_BINARY, - 'text' => self::TYPE_TEXT, - 'varchar' => self::TYPE_STRING, - 'string' => self::TYPE_STRING, - 'char' => self::TYPE_CHAR, - 'datetime' => self::TYPE_DATETIME, - 'year' => self::TYPE_DATE, - 'date' => self::TYPE_DATE, - 'time' => self::TYPE_TIME, - 'timestamp' => self::TYPE_TIMESTAMP, - 'enum' => self::TYPE_STRING, - 'varbinary' => self::TYPE_BINARY, - '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 unique indexes for the given table. * @@ -449,15 +418,9 @@ protected function getCreateTableSql(TableSchemaInterface $table): string private function loadColumnSchema(array $info): ColumnSchemaInterface { $dbType = $info['type']; - $type = $this->getColumnType($dbType, $info); - $isUnsigned = stripos($dbType, 'unsigned') !== false; /** @psalm-var ColumnInfoArray $info */ - $column = $this->createColumnSchema($type, unsigned: $isUnsigned); + $column = $this->getColumnFactory()->fromDefinition($dbType); $column->name($info['field']); - $column->enumValues($info['enum_values'] ?? null); - $column->size($info['size'] ?? null); - $column->precision($info['precision'] ?? null); - $column->scale($info['scale'] ?? null); $column->allowNull($info['null'] === 'YES'); $column->primaryKey(str_contains($info['key'], 'PRI')); $column->autoIncrement(stripos($info['extra'], 'auto_increment') !== false); @@ -470,7 +433,7 @@ private function loadColumnSchema(array $info): ColumnSchemaInterface empty($extra) && !empty($info['extra_default_value']) && !str_starts_with($info['extra_default_value'], '\'') - && in_array($type, [ + && in_array($column->getType(), [ self::TYPE_CHAR, self::TYPE_STRING, self::TYPE_TEXT, self::TYPE_DATETIME, self::TYPE_TIMESTAMP, self::TYPE_TIME, self::TYPE_DATE, ], true) @@ -488,41 +451,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])) { - if ($dbType === 'enum') { - preg_match_all("/'([^']*)'/", $matches[2], $values); - $info['enum_values'] = $values[1]; - } else { - $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 === '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..bfe58602 --- /dev/null +++ b/tests/ColumnFactoryTest.php @@ -0,0 +1,34 @@ + 1]]; + + return $definitions; + } +} diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 088cba82..8485201a 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -15,6 +15,7 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Mysql\Column; +use Yiisoft\Db\Mysql\Column\ColumnFactory; use Yiisoft\Db\Mysql\Schema; use Yiisoft\Db\Mysql\Tests\Support\TestTrait; use Yiisoft\Db\Query\Query; @@ -563,4 +564,12 @@ public function testInsertDefaultValues() 'numeric_col' => '-33.22', ], $row); } + + public function testGetColumnFactory(): void + { + $db = $this->getConnection(); + $factory = $db->getSchema()->getColumnFactory(); + + $this->assertInstanceOf(ColumnFactory::class, $factory); + } }