Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Realize column factory #314

Merged
merged 6 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
65 changes: 65 additions & 0 deletions src/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Sqlite\Column;

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

class ColumnFactory extends AbstractColumnFactory
Tigrov marked this conversation as resolved.
Show resolved Hide resolved
{
/**
* Mapping from physical column types (keys) to abstract column types (values).
*
* @var string[]
*
* @psalm-suppress MissingClassConstType
*/
private const TYPE_MAP = [
'bool' => 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;
}
}
87 changes: 9 additions & 78 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
*
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\Sqlite\Tests;

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

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

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

/** @dataProvider \Yiisoft\Db\Sqlite\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\Sqlite\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\Sqlite\Tests\Provider;

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
['bool', 'boolean', BooleanColumnSchema::class],
['boolean', 'boolean', BooleanColumnSchema::class],
['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],
['blob', '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]];
$definitions[] = ['tinyint(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 @@ -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;
Expand Down Expand Up @@ -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);
}
}
Loading