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 #864

Merged
merged 13 commits into from
Sep 1, 2024
52 changes: 0 additions & 52 deletions src/Schema/AbstractSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@
use Yiisoft\Db\Constraint\IndexConstraint;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Constant\GettypeResult;
use Yiisoft\Db\Schema\Column\BinaryColumnSchema;
use Yiisoft\Db\Schema\Column\BitColumnSchema;
use Yiisoft\Db\Schema\Column\BooleanColumnSchema;
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
use Yiisoft\Db\Schema\Column\DoubleColumnSchema;
use Yiisoft\Db\Schema\Column\IntegerColumnSchema;
use Yiisoft\Db\Schema\Column\JsonColumnSchema;
use Yiisoft\Db\Schema\Column\StringColumnSchema;
use Yiisoft\Db\Schema\Column\BigIntColumnSchema;

use function gettype;
use function is_array;
Expand Down Expand Up @@ -374,49 +365,6 @@ protected function findTableNames(string $schema): array
throw new NotSupportedException(static::class . ' does not support fetching all table names.');
}

/**
* Creates a column schema for the database.
*
* This method may be overridden by child classes to create a DBMS-specific column schema.
*
* @param string $type The abstract data type.
* @param mixed ...$info The column information.
* @psalm-param array{unsigned?: bool} $info The set of parameters may be different for a specific DBMS.
*
* @return ColumnSchemaInterface
*/
protected function createColumnSchema(string $type, mixed ...$info): ColumnSchemaInterface
{
$isUnsigned = !empty($info['unsigned']);

$column = $this->createColumnSchemaFromType($type, $isUnsigned);
$column->unsigned($isUnsigned);

return $column;
}

protected function createColumnSchemaFromType(string $type, bool $isUnsigned = false): ColumnSchemaInterface
{
return match ($type) {
SchemaInterface::TYPE_BOOLEAN => new BooleanColumnSchema($type),
SchemaInterface::TYPE_BIT => new BitColumnSchema($type),
SchemaInterface::TYPE_TINYINT => new IntegerColumnSchema($type),
SchemaInterface::TYPE_SMALLINT => new IntegerColumnSchema($type),
SchemaInterface::TYPE_INTEGER => PHP_INT_SIZE !== 8 && $isUnsigned
? new BigIntColumnSchema($type)
: new IntegerColumnSchema($type),
SchemaInterface::TYPE_BIGINT => PHP_INT_SIZE !== 8 || $isUnsigned
? new BigIntColumnSchema($type)
: new IntegerColumnSchema($type),
SchemaInterface::TYPE_DECIMAL => new DoubleColumnSchema($type),
SchemaInterface::TYPE_FLOAT => new DoubleColumnSchema($type),
SchemaInterface::TYPE_DOUBLE => new DoubleColumnSchema($type),
SchemaInterface::TYPE_BINARY => new BinaryColumnSchema($type),
SchemaInterface::TYPE_JSON => new JsonColumnSchema($type),
default => new StringColumnSchema($type),
};
}

/**
* Returns the metadata of the given type for all tables in the given schema.
*
Expand Down
34 changes: 32 additions & 2 deletions src/Schema/Column/AbstractColumnSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Yiisoft\Db\Constant\PhpType;

use function is_array;

/**
* Represents the metadata of a column in a database table.
*
Expand All @@ -21,11 +23,10 @@
* ```php
* use Yiisoft\Db\Schema\ColumnSchema;
*
* $column = (new ColumnSchema())
* $column = (new IntegerColumnSchema())
* ->name('id')
* ->allowNull(false)
* ->dbType('int(11)')
* ->phpType('integer')
* ->type('integer')
* ->defaultValue(0)
* ->autoIncrement()
Expand Down Expand Up @@ -182,6 +183,35 @@ public function isUnsigned(): bool
return $this->unsigned;
}

public function load(array $info): static
{
foreach ($info as $key => $value) {
/**
* @psalm-suppress PossiblyInvalidCast
* @psalm-suppress RiskyCast
*/
match ($key) {
'allow_null' => $this->allowNull((bool) $value),
'auto_increment' => $this->autoIncrement((bool) $value),
'comment' => $this->comment($value !== null ? (string) $value : null),
'computed' => $this->computed((bool) $value),
'db_type' => $this->dbType($value !== null ? (string) $value : null),
'default_value' => $this->defaultValue($value),
'enum_values' => $this->enumValues(is_array($value) ? $value : null),
'extra' => $this->extra($value !== null ? (string) $value : null),
'name' => $this->name($value !== null ? (string) $value : null),
'primary_key' => $this->primaryKey((bool) $value),
'precision' => $this->precision($value !== null ? (int) $value : null),
'scale' => $this->scale($value !== null ? (int) $value : null),
'size' => $this->size($value !== null ? (int) $value : null),
'unsigned' => $this->unsigned((bool) $value),
default => null,
};
}

return $this;
}

public function name(string|null $name): static
{
$this->name = $name;
Expand Down
137 changes: 137 additions & 0 deletions src/Schema/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Schema\Column;

use Yiisoft\Db\Schema\SchemaInterface;

use function explode;
use function preg_match;
use function str_ireplace;
use function stripos;
use function strlen;
use function strtolower;
use function substr;
use function trim;

use const PHP_INT_SIZE;

/**
* The default implementation of the {@see ColumnFactoryInterface}.
*
* @psalm-import-type ColumnInfo from ColumnSchemaInterface
* @psalm-suppress MixedArgumentTypeCoercion
*/
class ColumnFactory implements ColumnFactoryInterface
{
public function fromDbType(string $dbType, array $info = []): ColumnSchemaInterface
{
$info['db_type'] = $dbType;
$type = $info['type'] ?? $this->getType($dbType, $info);

return $this->fromType($type, $info);
}

public function fromDefinition(string $definition, array $info = []): ColumnSchemaInterface
{
preg_match('/^(\w*)(?:\(([^)]+)\))?\s*/', $definition, $matches);

$dbType = strtolower($matches[1]);

if (isset($matches[2])) {
$values = explode(',', $matches[2]);
$info['size'] = (int) $values[0];
$info['precision'] = (int) $values[0];

if (isset($values[1])) {
$info['scale'] = (int) $values[1];
}
}

$extra = substr($definition, strlen($matches[0]));

if (!empty($extra)) {
if (stripos($extra, 'unsigned') !== false) {
$info['unsigned'] = true;
$extra = trim(str_ireplace('unsigned', '', $extra));
}

if (!empty($extra)) {
if (empty($info['extra'])) {
$info['extra'] = $extra;
} else {
/** @psalm-suppress MixedOperand */
$info['extra'] = $extra . ' ' . $info['extra'];
}
}
}

return $this->fromDbType($dbType, $info);
}

public function fromType(string $type, array $info = []): ColumnSchemaInterface
{
$column = match ($type) {
SchemaInterface::TYPE_BOOLEAN => new BooleanColumnSchema($type),
SchemaInterface::TYPE_BIT => new BitColumnSchema($type),
SchemaInterface::TYPE_TINYINT => new IntegerColumnSchema($type),
SchemaInterface::TYPE_SMALLINT => new IntegerColumnSchema($type),
SchemaInterface::TYPE_INTEGER => PHP_INT_SIZE !== 8 && !empty($info['unsigned'])
? new BigIntColumnSchema($type)

Check warning on line 81 in src/Schema/Column/ColumnFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L81

Added line #L81 was not covered by tests
: new IntegerColumnSchema($type),
SchemaInterface::TYPE_BIGINT => PHP_INT_SIZE !== 8 || !empty($info['unsigned'])
? new BigIntColumnSchema($type)
: new IntegerColumnSchema($type),
SchemaInterface::TYPE_DECIMAL => new DoubleColumnSchema($type),
SchemaInterface::TYPE_FLOAT => new DoubleColumnSchema($type),
SchemaInterface::TYPE_DOUBLE => new DoubleColumnSchema($type),
SchemaInterface::TYPE_BINARY => new BinaryColumnSchema($type),
SchemaInterface::TYPE_JSON => new JsonColumnSchema($type),
default => new StringColumnSchema($type),
};

return $column->load($info);
}

/**
* Get the abstract database type for a database column type.
*
* @param string $dbType The database column type.
* @param array $info The column information.
*
* @return string The abstract database type.
*
* @psalm-param ColumnInfo $info
*/
protected function getType(string $dbType, array $info = []): string
{
return $this->isType($dbType) ? $dbType : SchemaInterface::TYPE_STRING;
}

protected function isType(string $dbType): bool
{
return match ($dbType) {
SchemaInterface::TYPE_UUID,
SchemaInterface::TYPE_CHAR,
SchemaInterface::TYPE_STRING,
SchemaInterface::TYPE_TEXT,
SchemaInterface::TYPE_BINARY,
SchemaInterface::TYPE_BOOLEAN,
SchemaInterface::TYPE_TINYINT,
SchemaInterface::TYPE_SMALLINT,
SchemaInterface::TYPE_INTEGER,
SchemaInterface::TYPE_BIGINT,
SchemaInterface::TYPE_FLOAT,
SchemaInterface::TYPE_DOUBLE,
SchemaInterface::TYPE_DECIMAL,
SchemaInterface::TYPE_MONEY,
SchemaInterface::TYPE_DATETIME,
SchemaInterface::TYPE_TIMESTAMP,
SchemaInterface::TYPE_TIME,
SchemaInterface::TYPE_DATE,
SchemaInterface::TYPE_JSON => true,
default => false,
};
}
}
44 changes: 44 additions & 0 deletions src/Schema/Column/ColumnFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Schema\Column;

/**
* The interface must be implemented by a column factory class. It should create a column schema for a database column
* type and initialize column information.
*
* @psalm-import-type ColumnInfo from ColumnSchemaInterface
*/
interface ColumnFactoryInterface
{
/**
* Creates a column schema for a database column type and initializes column information.
*
* @param string $dbType The database column type.
* @param array $info The column information.
*
* @psalm-param ColumnInfo $info The set of parameters may be different for a specific DBMS.
*/
public function fromDbType(string $dbType, array $info = []): ColumnSchemaInterface;

/**
* Creates a column schema for a database column definition and initializes column information.
*
* @param string $definition The database column definition.
* @param array $info The column information.
*
* @psalm-param ColumnInfo $info The set of parameters may be different for a specific DBMS.
*/
public function fromDefinition(string $definition, array $info = []): ColumnSchemaInterface;

/**
* Creates a column schema for an abstract database type and initializes column information.
*
* @param string $type The abstract database type.
* @param array $info The column information.
*
* @psalm-param ColumnInfo $info The set of parameters may be different for a specific DBMS.
*/
public function fromType(string $type, array $info = []): ColumnSchemaInterface;
}
30 changes: 29 additions & 1 deletion src/Schema/Column/ColumnSchemaInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@
/**
* This interface defines a set of methods that must be implemented by a class that represents the column schema of a
* database table column.
*
* @psalm-type ColumnInfo = array{
* allow_null?: bool|string|null,
* auto_increment?: bool|string,
* comment?: string|null,
* computed?: bool|string,
* db_type?: string|null,
* default_value?: mixed,
* enum_values?: array|null,
* extra?: string|null,
* primary_key?: bool|string,
* name?: string|null,
* precision?: int|string|null,
* scale?: int|string|null,
* schema?: string|null,
* size?: int|string|null,
* table?: string|null,
* type?: string,
* unsigned?: bool|string,
* ...<string, mixed>
* }
*/
interface ColumnSchemaInterface
{
Expand Down Expand Up @@ -250,7 +271,14 @@ public function isPrimaryKey(): bool;
public function isUnsigned(): bool;

/**
* Sets the name of the column.
* Loads the column's schema information from an array.
*
* @psalm-param ColumnInfo $info
*/
public function load(array $info): static;

/**
* Sets a name of the column.
*
* ```php
* $columns = [
Expand Down
3 changes: 3 additions & 0 deletions src/Schema/SchemaInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Schema\Builder\ColumnInterface;
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;

/**
* Represents the schema for a database table.
Expand Down Expand Up @@ -228,6 +229,8 @@ interface SchemaInterface extends ConstraintSchemaInterface
*/
public function createColumn(string $type, array|int|string $length = null): ColumnInterface;

public function getColumnFactory(): ColumnFactoryInterface;

/**
* @return string|null The default schema name.
*/
Expand Down
Loading
Loading