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 6a8ceea commit 1ac8cc0
Show file tree
Hide file tree
Showing 12 changed files with 427 additions and 55 deletions.
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),

Check warning on line 197 in src/Schema/Column/AbstractColumnSchema.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/AbstractColumnSchema.php#L194-L197

Added lines #L194 - L197 were not covered by tests
'db_type' => $this->dbType($value !== null ? (string) $value : null),
'default_value' => $this->defaultValue($value),

Check warning on line 199 in src/Schema/Column/AbstractColumnSchema.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/AbstractColumnSchema.php#L199

Added line #L199 was not covered by tests
'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),

Check warning on line 203 in src/Schema/Column/AbstractColumnSchema.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/AbstractColumnSchema.php#L203

Added line #L203 was not covered by tests
'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
135 changes: 135 additions & 0 deletions src/Schema/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?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
*/
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 {
$info['extra'] = $extra . ' ' . $info['extra'];

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

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L63

Added line #L63 was not covered by tests
}
}
}

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 79 in src/Schema/Column/ColumnFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L79

Added line #L79 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

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

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L105

Added line #L105 was not covered by tests
{
return $this->isType($dbType) ? $dbType : SchemaInterface::TYPE_STRING;

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

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L107

Added line #L107 was not covered by tests
}

protected function isType(string $dbType): bool

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

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L110

Added line #L110 was not covered by tests
{
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,
};

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

View check run for this annotation

Codecov / codecov/patch

src/Schema/Column/ColumnFactory.php#L112-L133

Added lines #L112 - L133 were not covered by tests
}
}
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;
}
29 changes: 28 additions & 1 deletion src/Schema/Column/ColumnSchemaInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@
/**
* 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,
* precision?: int|string|null,
* scale?: int|string|null,
* schema?: string|null,
* size?: int|string|null,
* table?: string|null,
* type?: string,
* unsigned?: bool|string,
* }
*/
interface ColumnSchemaInterface
{
Expand Down Expand Up @@ -250,7 +270,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

0 comments on commit 1ac8cc0

Please sign in to comment.