Skip to content

Commit

Permalink
Realize column factory
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov committed Aug 13, 2024
1 parent 58007d4 commit 33713a1
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 80 deletions.
73 changes: 73 additions & 0 deletions src/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mysql\Column;

use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
use Yiisoft\Db\Schema\SchemaInterface;

class ColumnFactory extends \Yiisoft\Db\Schema\Column\ColumnFactory

Check failure on line 10 in src/Column/ColumnFactory.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

UndefinedClass

src/Column/ColumnFactory.php:10:29: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactory does not exist (see https://psalm.dev/019)

Check failure on line 10 in src/Column/ColumnFactory.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

UndefinedClass

src/Column/ColumnFactory.php:10:29: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactory does not exist (see https://psalm.dev/019)
{
/**
* Mapping from physical column types (keys) to abstract column types (values).
*
* @var string[]
*/
private const TYPE_MAP = [
'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,
'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;
}
}
89 changes: 9 additions & 80 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -77,48 +79,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

Check failure on line 87 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

UndefinedClass

src/Schema.php:87:41: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactoryInterface does not exist (see https://psalm.dev/019)

Check failure on line 87 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedInferredReturnType

src/Schema.php:87:41: MixedInferredReturnType: Could not verify return type 'Yiisoft\Db\Schema\Column\ColumnFactoryInterface' for Yiisoft\Db\Mysql\Schema::getColumnFactory (see https://psalm.dev/047)

Check failure on line 87 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

UndefinedClass

src/Schema.php:87:41: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactoryInterface does not exist (see https://psalm.dev/019)

Check failure on line 87 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedInferredReturnType

src/Schema.php:87:41: MixedInferredReturnType: Could not verify return type 'Yiisoft\Db\Schema\Column\ColumnFactoryInterface' for Yiisoft\Db\Mysql\Schema::getColumnFactory (see https://psalm.dev/047)
{
return new ColumnFactory();

Check failure on line 89 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedReturnStatement

src/Schema.php:89:16: MixedReturnStatement: Could not infer a return type (see https://psalm.dev/138)

Check failure on line 89 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MissingDependency

src/Schema.php:89:20: MissingDependency: Yiisoft\Db\Mysql\Column\ColumnFactory depends on class or interface yiisoft\db\schema\column\columnfactory that does not exist (see https://psalm.dev/157)

Check failure on line 89 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedReturnStatement

src/Schema.php:89:16: MixedReturnStatement: Could not infer a return type (see https://psalm.dev/138)

Check failure on line 89 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MissingDependency

src/Schema.php:89:20: MissingDependency: Yiisoft\Db\Mysql\Column\ColumnFactory depends on class or interface yiisoft\db\schema\column\columnfactory that does not exist (see https://psalm.dev/157)
}

/**
* Returns all unique indexes for the given table.
*
Expand Down Expand Up @@ -458,15 +428,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);

Check failure on line 432 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedAssignment

src/Schema.php:432:9: MixedAssignment: Unable to determine the type that $column is being assigned to (see https://psalm.dev/032)

Check failure on line 432 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

UndefinedClass

src/Schema.php:432:19: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactoryInterface does not exist (see https://psalm.dev/019)

Check failure on line 432 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedAssignment

src/Schema.php:432:9: MixedAssignment: Unable to determine the type that $column is being assigned to (see https://psalm.dev/032)

Check failure on line 432 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

UndefinedClass

src/Schema.php:432:19: UndefinedClass: Class, interface or enum named Yiisoft\Db\Schema\Column\ColumnFactoryInterface does not exist (see https://psalm.dev/019)
$column->name($info['field']);

Check failure on line 433 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedMethodCall

src/Schema.php:433:18: MixedMethodCall: Cannot determine the type of $column when calling method name (see https://psalm.dev/015)

Check failure on line 433 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedMethodCall

src/Schema.php:433:18: MixedMethodCall: Cannot determine the type of $column when calling method name (see https://psalm.dev/015)
$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');

Check failure on line 434 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedMethodCall

src/Schema.php:434:18: MixedMethodCall: Cannot determine the type of $column when calling method allowNull (see https://psalm.dev/015)

Check failure on line 434 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedMethodCall

src/Schema.php:434:18: MixedMethodCall: Cannot determine the type of $column when calling method allowNull (see https://psalm.dev/015)
$column->primaryKey(str_contains($info['key'], 'PRI'));

Check failure on line 435 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedMethodCall

src/Schema.php:435:18: MixedMethodCall: Cannot determine the type of $column when calling method primaryKey (see https://psalm.dev/015)

Check failure on line 435 in src/Schema.php

View workflow job for this annotation

GitHub Actions / PHP 8.0

MixedMethodCall

src/Schema.php:435:18: MixedMethodCall: Cannot determine the type of $column when calling method primaryKey (see https://psalm.dev/015)
$column->autoIncrement(stripos($info['extra'], 'auto_increment') !== false);
Expand All @@ -479,7 +443,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)
Expand All @@ -497,41 +461,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.
*
Expand Down
34 changes: 34 additions & 0 deletions tests/ColumnFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mysql\Tests;

use Yiisoft\Db\Mysql\Tests\Support\TestTrait;
use Yiisoft\Db\Tests\AbstractColumnFactoryTest;

/**
* @group mysql
*/
final class ColumnFactoryTest extends AbstractColumnFactoryTest
{
use TestTrait;

/** @dataProvider \Yiisoft\Db\Mysql\Tests\Provider\ColumnFactoryProvider::dbTypes */
public function testFromDbType(string $dbType, string $expectedType, string $expectedInstanceOf): void
{
parent::testFromDbType($dbType, $expectedType, $expectedInstanceOf);
}

/** @dataProvider \Yiisoft\Db\Mysql\Tests\Provider\ColumnFactoryProvider::definitions */
public function testFromDefinition(string $definition, string $expectedType, string $expectedInstanceOf, array $expectedInfo = []): void
{
parent::testFromDefinition($definition, $expectedType, $expectedInstanceOf, $expectedInfo);
}

/** @dataProvider \Yiisoft\Db\Mysql\Tests\Provider\ColumnFactoryProvider::types */
public function testFromType(string $type, string $expectedType, string $expectedInstanceOf): void
{
parent::testFromType($type, $expectedType, $expectedInstanceOf);
}
}
62 changes: 62 additions & 0 deletions tests/Provider/ColumnFactoryProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mysql\Tests\Provider;

use Yiisoft\Db\Schema\Column\BigIntColumnSchema;
use Yiisoft\Db\Schema\Column\BinaryColumnSchema;
use Yiisoft\Db\Schema\Column\BitColumnSchema;
use Yiisoft\Db\Schema\Column\BooleanColumnSchema;
use Yiisoft\Db\Schema\Column\DoubleColumnSchema;
use Yiisoft\Db\Schema\Column\IntegerColumnSchema;
use Yiisoft\Db\Schema\Column\JsonColumnSchema;
use Yiisoft\Db\Schema\Column\StringColumnSchema;

final class ColumnFactoryProvider extends \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider
{
public static function dbTypes(): array
{
return [
// db type, expected abstract type, expected instance of
['bit', 'bit', BitColumnSchema::class],
['tinyint', 'tinyint', IntegerColumnSchema::class],
['smallint', 'smallint', IntegerColumnSchema::class],
['mediumint', 'integer', IntegerColumnSchema::class],
['int', 'integer', IntegerColumnSchema::class],
['integer', 'integer', IntegerColumnSchema::class],
['bigint', 'bigint', IntegerColumnSchema::class],
['float', 'float', DoubleColumnSchema::class],
['real', 'float', DoubleColumnSchema::class],
['double', 'double', DoubleColumnSchema::class],
['decimal', 'decimal', DoubleColumnSchema::class],
['numeric', 'decimal', DoubleColumnSchema::class],
['char', 'char', StringColumnSchema::class],
['varchar', 'string', StringColumnSchema::class],
['string', 'string', StringColumnSchema::class],
['enum', 'string', StringColumnSchema::class],
['tinytext', 'text', StringColumnSchema::class],
['mediumtext', 'text', StringColumnSchema::class],
['longtext', 'text', StringColumnSchema::class],
['text', 'text', StringColumnSchema::class],
['varbinary', 'binary', BinaryColumnSchema::class],
['blob', 'binary', BinaryColumnSchema::class],
['longblob', 'binary', BinaryColumnSchema::class],
['year', 'date', StringColumnSchema::class],
['date', 'date', StringColumnSchema::class],
['time', 'time', StringColumnSchema::class],
['datetime', 'datetime', StringColumnSchema::class],
['timestamp', 'timestamp', StringColumnSchema::class],
['json', 'json', JsonColumnSchema::class],
];
}

public static function definitions(): array
{
$definitions = parent::definitions();

$definitions[] = ['bit(1)', 'boolean', BooleanColumnSchema::class, ['getSize' => 1]];

return $definitions;
}
}
9 changes: 9 additions & 0 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -562,4 +563,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);
}
}

0 comments on commit 33713a1

Please sign in to comment.