Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -0,0 +1,546 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Adapter\Driver;
use Exception;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\SchemaCreator\Collection\ColumnCollection;
use Xentral\Components\SchemaCreator\Collection\IndexCollection;
use Xentral\Components\SchemaCreator\Collection\TableOptionCollection;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Index\Constraint;
use Xentral\Components\SchemaCreator\Index\Index;
use Xentral\Components\SchemaCreator\Index\Primary;
use Xentral\Components\SchemaCreator\Index\Unique;
use Xentral\Components\SchemaCreator\Interfaces\PrimaryKeyInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
use Xentral\Components\SchemaCreator\Interfaces\DriverInterface;
use Xentral\Components\SchemaCreator\LineGenerator\Common\ColumnLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\ConstraintLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\IndexLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\TableOptionsGenerator;
use Xentral\Components\SchemaCreator\Option\TableOption;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
final class MysqlDriver implements DriverInterface
{
/** @var ColumnLineGenerator $columnGenerator */
private $columnGenerator;
/** @var IndexLineGenerator $indexGenerator */
private $indexGenerator;
/** @var TableOptionsGenerator $tableOptionsGenerator */
private $tableOptionsGenerator;
/** @var ConstraintLineGenerator $constraintGenerator */
private $constraintGenerator;
/**
* @param ColumnLineGenerator $columnGenerator
* @param IndexLineGenerator $indexGenerator
* @param TableOptionsGenerator $tableOptionsGenerator
* @param ConstraintLineGenerator $constraintLineGenerator
*/
public function __construct(
ColumnLineGenerator $columnGenerator,
IndexLineGenerator $indexGenerator,
TableOptionsGenerator $tableOptionsGenerator,
ConstraintLineGenerator $constraintLineGenerator
) {
$this->columnGenerator = $columnGenerator;
$this->indexGenerator = $indexGenerator;
$this->tableOptionsGenerator = $tableOptionsGenerator;
$this->constraintGenerator = $constraintLineGenerator;
}
/**
* @param ColumnCollection $columnCollection
* @param IndexCollection $indexCollection
*
* @throws Exception
* @return string
*/
private function generateTableColumns(ColumnCollection $columnCollection, IndexCollection $indexCollection): string
{
$columnDefinitions = [];
$hasAutoIncrement = $columnCollection->hasAutoIncrement();
$autoIncrementColumn = $columnCollection->getAutoIncrementColumn();
foreach ($columnCollection as $column) {
$columnDefinitions[] = $this->columnGenerator->generateLine($column);
$isFieldAsAutoIncrement = $hasAutoIncrement === true && $autoIncrementColumn->getField(
) === $column->getField();
if ($indexCollection !== null && $isFieldAsAutoIncrement === true && $indexCollection->hasPrimaryKey(
) === false) {
$primaryRef = $column->getField();
$indexCollection->add(new Primary([$primaryRef]));
$columnCollection->getIterator()->rewind();
}
}
return implode(",\n", $columnDefinitions);
}
/**
* @param IndexCollection $indexCollection
* @param ColumnCollection $targetColumns
*
* @throws EscapingException
*
* @return string
*/
private function generateTableIndexes(IndexCollection $indexCollection, ColumnCollection $targetColumns): string
{
$asIndexes = [];
foreach ($indexCollection as $tableIndex) {
/** @var IndexInterface $tableIndex */
if ($this->hasIndexReferencesInSchema($tableIndex->getReferences(), $targetColumns)) {
$asIndexes [] = $this->indexGenerator->generateLine($tableIndex);
}
}
return implode(",\n", $asIndexes);
}
/**
* @param TableOptionCollection $tableOptionCollection
*
* @throws EscapingException
*
* @return string
*/
private function generateTableOptions(TableOptionCollection $tableOptionCollection): string
{
$tableOptions = [];
foreach ($tableOptionCollection as $tableOption) {
/** @var TableOption $tableOption */
$tableOptions[$tableOption->getOption()] = $tableOption->getValue();
}
return $this->tableOptionsGenerator->generate($tableOptions);
}
/**
* @inheritDoc
*/
public function loadFromTable(string $table): TableSchema
{
$tableSchema = new TableSchema($table);
if ($columns = $this->columnGenerator->fetchColumnsFromDb($table)) {
foreach ($columns as $column) {
$type = strtolower($column['type']);
$type = $type === 'int' ? 'integer' : $type; //Cannot use 'Int' as class name as it is reserved
$type = $type === 'float' ? 'double' : $type; //Cannot use 'Float' as class name as it is reserved
$classType = '\\Xentral\\Components\\SchemaCreator\\Type\\' . ucfirst($type);
if (!class_exists($classType)) {
throw new SchemaCreatorInvalidArgumentException(sprintf('%s cannot be found', $classType));
}
if (!method_exists($classType, 'fromDBColumn')) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Method %s::fromDBColumn not found', $classType)
);
}
if (is_numeric($column['default']) && $this->hasImplemented($classType, 'NumericTypeInterface')) {
$column['default'] = (int)$column['default'];
}
if ($this->hasImplemented($classType, 'EnumAndSetInterface')) {
$column['references'] = explode(',', $column['references']);
}
$callback = call_user_func($classType . '::fromDBColumn', $column);
$tableSchema->addColumn($callback);
}
}
if ($indexes = $this->indexGenerator->fetchIndexesFromDb($table)) {
$constraints = $this->constraintGenerator->fetchConstraintsFromDb($table);
foreach ($indexes as $index) {
if ($index['name'] === PrimaryKeyInterface::INDEX_NAME) {
$tableSchema->addIndex(new Primary($index['columns']));
} elseif ( ($constraintKey = array_search($index['name'], array_column($constraints, 'name'), true)) !== false) {
$tableIndex = new Constraint(
$constraints[$constraintKey]['name'],
$constraints[$constraintKey]['columns'],
$constraints[$constraintKey]['reference_table'],
$constraints[$constraintKey]['reference_columns']
);
$tableSchema->addIndex($tableIndex);
} else {
$tableIndex = $index['unique'] === true ? new Unique($index['columns'], $index['name']) : new Index(
$index['columns'], $index['name']
);
$tableSchema->addIndex($tableIndex);
}
}
}
if ($options = $this->tableOptionsGenerator->fetchOptionsFromDb($table)) {
$tableSchema->addOption(TableOption::fromEngine($options['engine']));
$tableSchema->addOption(TableOption::fromCharset($options['table_charset']));
$tableSchema->addOption(TableOption::fromCollation($options['collation']));
$tableSchema->addOption(TableOption::fromComment($options['comment']));
}
return $tableSchema;
}
/**
* @param string $class
* @param string $needleInterface
*
* @return bool
*/
private function hasImplemented(string $class, string $needleInterface): bool
{
if ($interfaces = class_implements($class)) {
foreach ($interfaces as $interface) {
$interface_exploded = explode('\\', $interface);
$name = array_pop($interface_exploded);
if ($name === $needleInterface) {
return true;
}
}
}
return false;
}
/**
* @param array $references
* @param ColumnCollection $targetColumns
*
* @return bool
*/
private function hasIndexReferencesInSchema(array $references, ColumnCollection $targetColumns): bool
{
$columns = [];
foreach ($targetColumns as $configuredColumns) {
$columns[] = $configuredColumns->getField();
}
foreach ($references as $reference) {
if (!in_array(sprintf('%s', $reference), $columns, true)) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Column name `%s` missing in the table', $reference)
);
}
}
return true;
}
/**
* @param array $targetSchema
* @param array $currentSchema
*
* @return bool
*/
private function checkColumnNeedsUpdate(array $targetSchema, array $currentSchema): bool
{
$diff = array_diff_assoc($targetSchema, $currentSchema);
return count($diff) > 0;
}
/**
* @param TableSchema $currentSchema
* @param TableSchema $targetSchema
*
* @throws EscapingException
*
* @return string
*/
private function generateColumnsDiff(TableSchema $currentSchema, TableSchema $targetSchema): string
{
$alterColumnParts = [];
$targetColumns = $targetSchema->getColumns();
$currentColumns = $currentSchema->getColumns();
$currentIndexes = $currentSchema->getIndexes();
$hasAutoIncrement = $currentColumns->hasAutoIncrement();
$autoIncrementColumn = $currentColumns->getAutoIncrementColumn();
foreach ($currentColumns as $schemaKey => $currentColumn) {
$isFieldAsAutoIncrement = $hasAutoIncrement === true && $autoIncrementColumn->getField(
) === $currentColumn->getField();
if ($currentIndexes !== null && $isFieldAsAutoIncrement === true && $currentIndexes->hasPrimaryKey(
) === false) {
$primaryReference = $currentColumn->getField();
$currentIndexes->add(new Primary([$primaryReference]));
}
}
$targetFields = $targetColumns->getFields();
$currentFields = $currentColumns->getFields();
$newFields = array_diff($targetFields, $currentFields);
foreach ($targetColumns as $schemaKey => $schemaColumn) {
/** @var ColumnInterface $schemaColumn */
$field = $schemaColumn->getField();
if (in_array($field, $newFields, true)) {
$alterColumnParts[] = sprintf('ADD %s', $this->columnGenerator->generateLine($schemaColumn));
} else {
$needsUpdate = $this->checkColumnNeedsUpdate(
$this->columnGenerator->toArray($schemaColumn),
$this->columnGenerator->toArray($currentSchema->getColumnByName($field))
);
if ($needsUpdate) {
$alterColumnParts[] = sprintf('MODIFY %s', $this->columnGenerator->generateLine($schemaColumn));
}
}
}
return implode(', ', $alterColumnParts);
}
/**
* @param IndexInterface $targetIndex
* @param IndexInterface $currentIndex
*
* @return bool
*/
private function checkIndexNeedsUpdate(IndexInterface $targetIndex, IndexInterface $currentIndex): bool
{
$targetReferences = $targetIndex->getReferences();
$currentReferences = $currentIndex->getReferences();
if ($targetIndex->isUnique() !== $currentIndex->isUnique()) {
return true;
}
$diffColumns = array_diff($targetReferences, $currentReferences);
return count($diffColumns) > 0;
}
/**
* @param TableSchema $currentSchema
* @param TableSchema $targetSchema
*
* @throws Exception
* @return string
*/
private function generateIndexesDiff(TableSchema $currentSchema, TableSchema $targetSchema): string
{
$alterIndexParts = [];
$targetIndexes = $targetSchema->getIndexes();
$currentIndexes = $currentSchema->getIndexes();
$targetColumns = $targetSchema->getColumns();
// FIX MISSING PRIMARY KEY HERE
$this->generateTableColumns($targetColumns, $targetIndexes);
$targetIndexNames = $targetIndexes->getIndexNames();
$currentIndexNames = $currentIndexes->getIndexNames();
$newIndexes = array_diff($targetIndexNames, $currentIndexNames);
$removedIndexes = array_diff($currentIndexNames, $targetIndexNames);
if (count($removedIndexes) > 0) {
foreach ($removedIndexes as $removedIndexName) {
if ($removedIndexName !== 'PRIMARY' && !in_array($removedIndexName, $newIndexes, true)) {
foreach($currentIndexes as $currentIndex) {
if($currentIndex->getName() !== $removedIndexName) {
continue;
}
if($currentIndex->getType() === 'CONSTRAINT') {
$alterIndexParts[] = sprintf('DROP FOREIGN KEY %s', $this->indexGenerator->escape($removedIndexName));
break;
}
else {
$alterIndexParts[] = sprintf('DROP INDEX %s', $this->indexGenerator->escape($removedIndexName));
break;
}
}
}
}
}
foreach ($targetIndexes as $schemaKey => $schemaIndex) {
$indexName = $schemaIndex->getName();
$indexType = $schemaIndex->getType();
if (in_array($indexName, $newIndexes, true) &&
$this->hasIndexReferencesInSchema($schemaIndex->getReferences(), $targetColumns) === true) {
$alterIndexParts[] = sprintf('ADD %s', $this->indexGenerator->generateLine($schemaIndex));
} else {
$needsUpdate = $this->checkIndexNeedsUpdate($schemaIndex, $currentSchema->getIndexByName($indexName));
if ($needsUpdate) {
if ($schemaIndex instanceof PrimaryKeyInterface) {
$references = $schemaIndex->getReferences();
// CHECK IF one of the reference has AutoIncrement
$currentPrimary = $currentSchema->getIndexByName('PRIMARY');
if (null !== $currentPrimary) {
$currentReferences = $currentPrimary->getReferences();
$columnModified = null;
foreach ($currentReferences as $columnName) {
$column = $currentSchema->getColumnByName($columnName);
$options = $column->getOptions();
if (array_key_exists('extra', $options) && in_array(
$options['extra'],
['ai', 'AUTO_INCREMENT']
)) {
$columnModified = sprintf(
'MODIFY %s',
$this->columnGenerator->generateLine($column)
);
$columnModifiedWithoutAi = str_replace('AUTO_INCREMENT', '', $columnModified);
$alterIndexParts[] = trim($columnModifiedWithoutAi);
break;
}
}
$alterIndexParts[] = 'DROP PRIMARY KEY';
}
$reference = implode(
',',
array_map(
function ($reference) {
return $this->indexGenerator->escape($reference);
},
$references
)
);
$alterIndexParts[] = sprintf('ADD PRIMARY KEY (%s)', $reference);
} else {
if($indexType === 'CONSTRAINT') {
$alterIndexParts[] = sprintf('DROP FOREIGN KEY %s', $this->indexGenerator->escape($indexName));
}
else {
$alterIndexParts[] = sprintf('DROP INDEX %s', $this->indexGenerator->escape($indexName));
}
$alterIndexParts[] = sprintf('ADD %s', $this->indexGenerator->generateLine($schemaIndex));
}
}
}
}
return implode(', ', $alterIndexParts);
}
/**
* @param TableSchema $currentSchema
* @param TableSchema $targetSchema
*
* @throws Exception
* @return string
*/
private function generateOptionsDiff(TableSchema $currentSchema, TableSchema $targetSchema): string
{
$targetOptions = $targetSchema->getOptions();
if ($targetOptions->getIterator()->count() === 0) {
return '';
}
$currentOptions = $currentSchema->getOptions();
$sqlOptions = [];
$charset = null;
foreach ($targetOptions as $option) {
$currentTableOption = $currentOptions->getTableOption($option->getOption());
if ($currentTableOption === null || $currentTableOption->getValue() !== $option->getValue()) {
if ($option->getOption() === 'collation' && ($charsetOption = $currentOptions->getTableOption(
'charset'
))) {
$charset = $charsetOption->getValue();
}
$sqlOptions[] = $this->tableOptionsGenerator->alterOptions($option, $charset);
}
}
return implode(' ', $sqlOptions);
}
/**
* @inheritDoc
*/
public function getTableDefinition(TableSchema $schema): string
{
$name = $schema->getTable();
$columns = $this->generateTableColumns(
$schema->getColumns(),
$schema->getIndexes()
);
$keys = $this->generateTableIndexes($schema->getIndexes(), $schema->getColumns());
$columns = rtrim($columns, ',');
if (!empty($keys)) {
$keys = ',' . PHP_EOL . $keys;
}
$definition = sprintf("(\n%s\n)", $columns . $keys);
$defFooter = $this->generateTableOptions($schema->getOptions());
$definition .= $defFooter;
return sprintf('CREATE TABLE IF NOT EXISTS %s %s', $this->indexGenerator->escape($name), $definition);
}
/**
* @param string $table
*
* @throws EscapingException
*
* @return string
*/
private function generateAlterTable(string $table): string
{
return sprintf('ALTER TABLE %s ', $this->indexGenerator->escape($table));
}
/**
* @inheritDoc
*/
public function generateTableSchemaDiff(TableSchema $currentSchema, TableSchema $targetSchema): string
{
$sql = '';
$alterColumns = $this->generateColumnsDiff($currentSchema, $targetSchema);
$alterIndexes = $this->generateIndexesDiff($currentSchema, $targetSchema);
$alterOptions = $this->generateOptionsDiff($currentSchema, $targetSchema);
if (empty($alterColumns) && empty($alterIndexes) && empty($alterOptions)) {
return $sql;
}
if (!empty($alterColumns)) {
$sql .= $alterColumns . ', ';
}
if (!empty($alterIndexes)) {
$sql .= $alterIndexes . ', ';
}
$table = $targetSchema->getTable();
$alterTable = $this->generateAlterTable($table);
if (!empty($alterOptions)) {
$alterTable .= $alterOptions;
if (!empty($sql)) {
$alterTable .= ', ';
}
}
$sql = substr_replace($sql, ';', -2);
return $alterTable . $sql;
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator;
use Xentral\Components\SchemaCreator\Adapter\Driver\MysqlDriver;
use Xentral\Components\SchemaCreator\Collection\SchemaCollection;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorMissingDriverException;
use Xentral\Components\SchemaCreator\LineGenerator\Common\ColumnLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\ConstraintLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\IndexLineGenerator;
use Xentral\Components\SchemaCreator\LineGenerator\Common\TableOptionsGenerator;
use Xentral\Core\DependencyInjection\ServiceContainer;
final class Bootstrap
{
/**
* @return array
*/
public static function registerServices(): array
{
return [
'SchemaCreator' => 'onInitSchemaCreator',
];
}
/**
* @param SchemaCollection $collection
*
* @return SchemaCollection
*/
public static function registerTableSchemas(SchemaCollection $collection): SchemaCollection
{
return $collection;
}
/**
* @param ServiceContainer $container
*
* @return SchemaCreator
*/
public static function onInitSchemaCreator(ServiceContainer $container): SchemaCreator
{
$db = $container->get('Database');
$versionString = $db->fetchValue('SELECT VERSION()');
$databaseDetector = new DatabaseDetector(new DatabaseVersionStringParser($versionString));
if (!$databaseDetector->isMariaDb() && !$databaseDetector->isMySQL()) {
throw new SchemaCreatorMissingDriverException('Unknown Database Driver');
}
$driver = new MysqlDriver(
new ColumnLineGenerator($db),
new IndexLineGenerator($db),
new TableOptionsGenerator($db),
new ConstraintLineGenerator($db)
);
return new SchemaCreator($db, $driver);
}
}
@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Collection;
use ArrayIterator;
use IteratorAggregate;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class ColumnCollection implements IteratorAggregate
{
/** @var array|ColumnInterface[] */
private $values = [];
/** @var null|ColumnInterface */
private $autoIncrementColumn;
/**
* @param array $values
*/
public function __construct(array $values = [])
{
foreach ($values as $value) {
$this->add($value);
}
}
/**
* @param ColumnInterface $field
*
* @return void
*/
public function add(ColumnInterface $field): void
{
if ($this->hasColumn($field->getField()) === true) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Column name `%s` already added', $field->getField())
);
}
$options = $field->getOptions();
if (array_key_exists('extra', $options) && $options['extra'] !== null) {
if ($options['extra'] === 'AUTO_INCREMENT' || $options['extra'] === 'ai') {
$this->autoIncrementColumn = $field;
}
}
$this->values[] = $field;
}
/**
* @param string $column
*
* @return bool
*/
public function hasColumn(string $column): bool
{
/** @var ColumnInterface $configuredColumns */
foreach ($this->values as $configuredColumns) {
if ($column === $configuredColumns->getField()) {
return true;
}
}
return false;
}
/**
* @return array|string[]
*/
public function getFields(): array
{
$fields = [];
foreach ($this->values as $column) {
$fields[] = $column->getField();
}
return $fields;
}
/**
* @return ArrayIterator|ColumnInterface[]
*/
public function getIterator()
{
return new ArrayIterator($this->values);
}
/***
* @return bool
*/
public function hasAutoIncrement(): bool
{
return null !== $this->autoIncrementColumn;
}
/**
* @return ColumnInterface|null
*/
public function getAutoIncrementColumn(): ?ColumnInterface
{
return $this->autoIncrementColumn;
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Collection;
use ArrayIterator;
use IteratorAggregate;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\PrimaryKeyInterface;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class IndexCollection implements IteratorAggregate
{
/** @var array|IndexInterface[] */
private $values = [];
/**
* @param array $values
*/
public function __construct(array $values = [])
{
foreach ($values as $value) {
$this->add($value);
}
}
/**
* @return bool
*/
public function hasPrimaryKey(): bool
{
foreach ($this->values as $configuredKeys) {
if ($configuredKeys instanceof PrimaryKeyInterface) {
return true;
}
}
return false;
}
/**
* @param string $key
*
* @return bool
*/
public function hasIndex(string $key): bool
{
/** @var IndexInterface $configuredKeys */
foreach ($this->values as $configuredKeys) {
if ($key === $configuredKeys->getName()) {
return true;
}
}
return false;
}
/**
* @param IndexInterface $key
*
* @return void
*/
public function add(IndexInterface $key): void
{
if ($this->hasIndex($key->getName()) === true) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Key name `%s` already added', $key->getName())
);
}
$this->values[] = $key;
}
/**
* @return array
*/
public function getIndexNames(): array
{
$indexes = [];
foreach ($this->values as $index) {
$indexes[] = $index instanceof PrimaryKeyInterface ? 'PRIMARY' : $index->getName();
}
return $indexes;
}
/**
* @return ArrayIterator|IndexInterface[]
*/
public function getIterator()
{
return new ArrayIterator($this->values);
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Collection;
use ArrayIterator;
use IteratorAggregate;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
final class SchemaCollection implements IteratorAggregate
{
/** @var array|TableSchema[] */
private $values = [];
/**
* @param TableSchema $schema
*/
public function add(TableSchema $schema): void
{
$this->values[] = $schema;
}
/**
* @return ArrayIterator|TableSchema[]
*/
public function getIterator()
{
return new ArrayIterator($this->values);
}
}
@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Collection;
use ArrayIterator;
use IteratorAggregate;
use Xentral\Components\SchemaCreator\Option\TableOption;
final class TableOptionCollection implements IteratorAggregate
{
/** @var array|TableOption[] */
private $values = [];
/**
* @param TableOption $option
*/
public function add(TableOption $option): void
{
$this->values[] = $option;
}
/**
* @param string $option
*
* @return void
*/
public function remove(string $option): void
{
foreach ($this->values as $key => $tableOption) {
if ($tableOption->getOption() === $option) {
unset($this->values[$key]);
}
}
$this->values = array_values($this->values);
}
/**
* @param string $option
*
* @return TableOption|null
*/
public function getTableOption(string $option): ?TableOption
{
foreach ($this->values as $tableOption) {
if ($tableOption->getOption() === $option) {
return $tableOption;
}
}
return null;
}
/**
* @param string $option
*
* @return bool
*/
public function hasOption(string $option): bool
{
foreach ($this->values as $tableOption) {
if ($tableOption->getOption() === $option) {
return true;
}
}
return false;
}
/**
* @return ArrayIterator|TableOption[]
*/
public function getIterator()
{
return new ArrayIterator($this->values);
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator;
final class DatabaseDetector
{
/** @var DatabaseVersionStringParser $dbVersion */
private $dbVersion;
/**
* @param DatabaseVersionStringParser $dbVersion
*/
public function __construct(DatabaseVersionStringParser $dbVersion)
{
$this->dbVersion = $dbVersion;
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->dbVersion->getDriverVersion();
}
/**
* @return bool
*/
public function isMariaDb(): bool
{
return $this->dbVersion->isDriver('mariadb');
}
/**
* @return bool
*/
public function isMySQL(): bool
{
return $this->dbVersion->isDriver('mysql');
}
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator;
use InvalidArgumentException;
final class DatabaseVersionStringParser
{
/** @var string */
private const MYSQL_DB_TYPE = 'mysql';
/** @var string */
private const MARIA_DB_TYPE = 'mariadb';
/** @var string $db */
private $stringVersion;
/**
* @param string $stringVersion
*/
public function __construct(string $stringVersion)
{
if (empty($stringVersion)) {
throw new InvalidArgumentException('String version cannot be Empty');
}
$this->stringVersion = strtolower($stringVersion);
}
/**
* @param string $driver
*
* @throw InvalidArgumentException
*
* @return bool
*/
public function isDriver(string $driver): bool
{
if ($driver !== self::MARIA_DB_TYPE && strripos($this->stringVersion, 'maria') !== false) {
return false;
}
if ($driver === self::MARIA_DB_TYPE) {
return strripos($this->stringVersion, 'maria') !== false;
}
if ($driver !== self::MYSQL_DB_TYPE) {
throw new InvalidArgumentException(sprintf('%s is currently not supported', $driver));
}
return true;
}
/**
* @return string
*/
public function getDriverVersion(): string
{
$version = $this->isDriver(self::MARIA_DB_TYPE) ? substr($this->stringVersion, 0, 4) : substr(
$this->stringVersion,
0,
3
);
if (empty($version) || !is_numeric($version[0])) {
throw new InvalidArgumentException('Unknown Database Driver Version');
}
return $version;
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use RuntimeException;
class LineGeneratorException extends RuntimeException implements SchemaCreatorTableExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use RuntimeException as SplRuntimeException;
class SchemaCreatorColumnValidatorException extends SplRuntimeException implements SchemaCreatorTableExceptionInterface
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use InvalidArgumentException as SplInvalidArgumentException;
class SchemaCreatorInvalidArgumentException extends SplInvalidArgumentException
implements SchemaCreatorTableExceptionInterface
{
}
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use InvalidArgumentException as SplInvalidArgumentException;
class SchemaCreatorMissingDriverException extends SplInvalidArgumentException implements SchemaCreatorTableExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use RuntimeException as SplRuntimeException;
final class SchemaCreatorTableException extends SplRuntimeException implements SchemaCreatorTableExceptionInterface
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Exception;
use Xentral\Core\Exception\ComponentExceptionInterface;
interface SchemaCreatorTableExceptionInterface extends ComponentExceptionInterface
{
}
@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class Constraint implements IndexInterface
{
/** @var string $type */
private $type = 'CONSTRAINT';
/** @var string $foreignKey */
private $foreignKey;
/** @var string $parentTable */
private $parentTable;
/** @var string $parentId */
private $parentId;
/** @var array $cascadeOn */
private $cascadeOn;
/** @var string $name */
private $name;
/**
* @param string $name
* @param array $foreignKey
* @param string $parentTable
* @param array $parentId
* @param array $cascadeOn
*/
public function __construct(
string $name,
array $foreignKey,
string $parentTable,
array $parentId,
array $cascadeOn = []
) {
$this->parentId = $parentId;
$this->foreignKey = $foreignKey;
$this->cascadeOn = $cascadeOn;
$this->parentTable = $parentTable;
$this->name = $name;
}
/**
* @return array
*/
public function getParenId(): array
{
return $this->parentId;
}
/**
* @return string
*/
public function getParentTable(): string
{
return $this->parentTable;
}
/**
* @return array
*/
public function getCascadeOn(): array
{
return $this->cascadeOn;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @return string
*/
public function getForeignKey(): array
{
return $this->foreignKey;
}
/**
* @inheritDoc
*/
public function getName(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getReferences(): array
{
return [];
}
/**
* @inheritDoc
*/
public function isUnique(): bool
{
return false;
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class Fulltext implements IndexInterface
{
/** @var string $type */
private $type = 'FULLTEXT';
/** @var string|null $name */
private $name;
/** @var array $references */
private $references;
/**
* @param array $references
* @param string|null $name
*/
public function __construct(array $references, ?string $name = null)
{
$this->name = $name;
$this->references = $references;
}
public function getType(): string
{
return $this->type;
}
public function getName(): string
{
if (empty($this->name)) {
return 'fulltextkey_'.implode('_', array_map('strtolower', $this->references));
}
return $this->name;
}
public function getReferences(): array
{
return $this->references;
}
public function isUnique(): bool
{
return false;
}
}
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class Index implements IndexInterface
{
/** @var string $type */
private $type = 'INDEX';
/** @var string|null $name */
private $name;
/** @var array $references */
private $references;
/**
* @param array $references
* @param string|null $name
*/
public function __construct(array $references, ?string $name = null)
{
$this->name = $name;
$this->references = $references;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getName(): string
{
if (empty($this->name)) {
return 'index_'.implode('_', array_map('strtolower', $this->references));
}
return $this->name;
}
/**
* @inheritDoc
*/
public function getReferences(): array
{
return $this->references;
}
/**
* @inheritDoc
*/
public function isUnique(): bool
{
return false;
}
}
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Interfaces\PrimaryKeyInterface;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class Primary implements IndexInterface, PrimaryKeyInterface
{
/** @var string $type */
private $type = 'PRIMARY KEY';
/** @var array $references */
private $references;
/**
* @param array $references
*/
public function __construct(array $references)
{
$this->references = $references;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getName(): string
{
return 'PRIMARY';
}
/**
* @inheritDoc
*/
public function getReferences(): array
{
return $this->references;
}
/**
* @inheritDoc
*/
public function isUnique(): bool
{
return false;
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
use Xentral\Components\SchemaCreator\Interfaces\UniqueIndexInterface;
final class Unique implements IndexInterface, UniqueIndexInterface
{
/** @var string $type */
private $type = UniqueIndexInterface::TYPE;
/** @var string|null $name */
private $name;
/** @var array $references */
private $references;
/**
* @param array $references
* @param string|null $name
*/
public function __construct(array $references, ?string $name = null)
{
$this->name = $name;
$this->references = $references;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getName(): string
{
if (empty($this->name)) {
return 'unique_'.implode('_', array_map('strtolower', $this->references));
}
return $this->name;
}
/**
* @inheritDoc
*/
public function getReferences(): array
{
return $this->references;
}
/**
* @inheritDoc
*/
public function isUnique(): bool
{
return true;
}
}
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface CharTypeInterface
{
/**
* @return int
*/
public function getLength(): int;
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface ColumnInterface
{
/**
* @var null|string[]
*/
public const DEFAULT_PARAMS = [
'default' => null,
'charset' => null,
'collate' => null,
'comment' => null,
'nullable' => true,
'extra' => null,
];
/**
* @return string
*/
public function getField(): string;
/**
* @return string
*/
public function getType(): string;
/**
* @return array
*/
public function getOptions(): array;
/**
* @return bool
*/
public function isNullable() : bool;
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface ColumnTextInterface
{
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface DateAndTimeColumnInterface
{
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface DecimalTypeInterface
{
/**
* @return int
*/
public function getDecimals(): int;
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\SchemaCreator\Exception\LineGeneratorException;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorTableException;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
use Exception;
interface DriverInterface
{
/**
* @param TableSchema $currentSchema
* @param TableSchema $targetSchema
*
* @throws EscapingException
* @throws Exception
*
* @return string
*/
public function generateTableSchemaDiff(TableSchema $currentSchema, TableSchema $targetSchema): string;
/**
* @param string $table
*
* @throws LineGeneratorException
* @throws SchemaCreatorTableException
* @throws Exception
*
* @return TableSchema
*/
public function loadFromTable(string $table): TableSchema;
/**
* @param TableSchema $currentSchema
*
* @throws EscapingException
* @throws Exception
*
* @return string
*/
public function getTableDefinition(TableSchema $currentSchema): ?string;
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface EnumAndSetInterface
{
/**
* @return string
*/
public function getReferences(): string;
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface IndexInterface
{
/**
* @return string
*/
public function getType(): string;
/**
* @return string
*/
public function getName(): string;
/**
* @return array
*/
public function getReferences(): array;
/**
* @return bool
*/
public function isUnique(): bool;
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface IntegerTypeInterface extends ColumnInterface
{
/**
* @return int
*/
public function getLength(): int;
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface NumericTypeInterface
{
public const NON_NEGATIV = 'UNSIGNED';
public const WITH_NEGATIV = 'SIGNED';
/**
* @return bool
*/
public function isUnsigned(): bool;
}
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface PrimaryKeyInterface extends UniqueIndexInterface
{
public const INDEX_NAME = 'PRIMARY';
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Interfaces;
interface UniqueIndexInterface
{
/** @var string */
public const TYPE = 'UNIQUE INDEX';
}
@@ -0,0 +1,420 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\LineGenerator\Common;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\Database\Exception\QueryFailureException;
use Xentral\Components\SchemaCreator\Exception\LineGeneratorException;
use Xentral\Components\SchemaCreator\Interfaces\CharTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\DecimalTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\EnumAndSetInterface;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
use Xentral\Components\SchemaCreator\Type\Datetime;
use Xentral\Components\SchemaCreator\Type\Timestamp;
use Xentral\Components\SchemaCreator\Type\Year;
final class ColumnLineGenerator
{
/** @var Database $db */
private $db;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->db = $database;
}
/**
* @param ColumnInterface $column
*
* @return string
*/
private function generateType(ColumnInterface $column): string
{
if ($column instanceof IntegerTypeInterface) {
$spec = $column->isUnsigned() ? NumericTypeInterface::NON_NEGATIV : NumericTypeInterface::WITH_NEGATIV;
if ($column instanceof DecimalTypeInterface) {
return sprintf(
'%s(%d, %d) %s',
$column->getType(),
$column->getLength(),
$column->getDecimals(),
$spec
);
}
return sprintf('%s(%d) %s', $column->getType(), $column->getLength(), $spec);
}
if ($column instanceof CharTypeInterface || $column instanceof Year) {
return sprintf('%s(%d)', $column->getType(), $column->getLength());
}
if ($column instanceof EnumAndSetInterface) {
return sprintf('%s(%s)', $column->getType(), $column->getReferences());
}
return $column->getType();
}
/**
* @param ColumnInterface $column
*
* @throws EscapingException
*
* @return string
*/
public function generateLine(ColumnInterface $column): string
{
$line = [];
$line[] = $this->db->escapeIdentifier($column->getField());
$line[] = $this->generateType($column);
if ($defaultLine = $this->getLineDefault($column)) {
$line[] = $defaultLine;
}
if ($charset = $this->generateCharset($column)) {
$line[] = $charset;
}
if ($collate = $this->generateCollate($column)) {
$line[] = $collate;
}
if ($comment = $this->generateComment($column)) {
$line[] = $comment;
}
$outLine = trim(implode(' ', $line));
if ($this->isAutoIncrementField($column)) {
$outLine .= ' AUTO_INCREMENT';
}
return $outLine;
}
/**
* @param $column
*
* @throws EscapingException
*
* @return string|null
*/
private function generateComment($column): ?string
{
return $this->hasOption('comment', $column) ? sprintf(
"COMMENT %s",
$this->db->escapeString($this->getOption('comment', $column))
) : null;
}
/**
* @param $column
*
* @throws EscapingException
*
* @return string|null
*/
private function generateCharset($column): ?string
{
return $this->hasOption('charset', $column) ? sprintf(
"CHARACTER SET %s",
$this->db->escapeString(
$this->getOption('charset', $column)
)
) : null;
}
/**
* @param $column
*
* @throws EscapingException
*
* @return string|null
*/
private function generateCollate($column): ?string
{
return $this->hasOption('collate', $column) ? sprintf(
'COLLATE %s',
$this->db->escapeString(
$this->getOption('collate', $column)
)
) : null;
}
/**
* @param ColumnInterface $column
*
* @return array
*/
public function toArray(ColumnInterface $column): array
{
return [
'field' => $column->getField(),
'type' => $column->getType(),
'length' => method_exists($column, 'getLength') ? $column->getLength() : null,
'decimals' => method_exists($column, 'getDecimals') ? $column->getDecimals() : null,
'unsigned' => method_exists($column, 'isUnsigned') ? $column->isUnsigned() : false,
'nullable' => $this->isNullable($column),
'default' => $this->getOption('default', $column),
'extra' => $this->getOption('extra', $column),
'references' => method_exists($column, 'getReferences') ? $column->getReferences() : null,
'sql_default' => $this->getLineDefault($column),
];
}
/**
* @param string $key
* @param ColumnInterface|null $column
*
* @return mixed|null
*/
public function getOption(string $key, ColumnInterface $column)
{
$options = $this->getAllOptions($column);
return $options[$key] ?? null;
}
/**
* @param ColumnInterface $column
*
* @return array
*/
private function getAllOptions(ColumnInterface $column): array
{
$options = $column->getOptions();
$options = array_merge(ColumnInterface::DEFAULT_PARAMS, $options);
if (array_key_exists('extra', $options) && $options['extra'] !== null) {
$options['extra'] = $this->extraMapping($options['extra']);
}
return $options;
}
/**
* @param string $key
* @param ColumnInterface $column
*
* @return bool
*/
public function hasOption(string $key, ColumnInterface $column): bool
{
$options = $this->getAllOptions($column);
return array_key_exists($key, $options) && $options[$key] !== null;
}
/**
* @param ColumnInterface $column
*
* @return bool
*/
private function isNullable(ColumnInterface $column): bool
{
return $column->isNullable();
}
/**
* @param ColumnInterface $column
*
* @return string
*/
private function getLineDefault(ColumnInterface $column): string
{
if ($this->isNullable($column) === true) {
$defaultValue = $column instanceof Timestamp ? 'NULL DEFAULT NULL' : 'DEFAULT NULL';
if ($this->getOption('default', $column) === null) {
return $defaultValue;
}
$customDefault = $this->getOption('default', $column);
if (($column instanceof Timestamp || $column instanceof Datetime) &&
$this->containMySQLTimeConstant($customDefault) === true) {
return sprintf('DEFAULT %s', strtoupper($this->getOption('default', $column)));
}
return sprintf("DEFAULT '%s'", $this->getOption('default', $column));
}
if (($column instanceof ColumnTextInterface) || $this->isAutoIncrementField($column) === true) {
return 'NOT NULL';
}
$default = $this->getDefault($column);
if ($this->hasOption('extra', $column)) {
$default .= ' ' . $this->getOption('extra', $column);
}
if ($this->isNullable($column) === false) {
$default = 'NOT NULL ' . $default;
}
return $default;
}
/**
* @param ColumnInterface $column
*
* @return string
*/
private function getDefault(ColumnInterface $column): string
{
if ($column instanceof NumericTypeInterface) {
return $this->hasOption('default', $column) ? sprintf(
"DEFAULT '%d'",
$this->getOption('default', $column)
) : '';
}
$isNullable = $this->isNullable($column);
$defaultSet = $this->getOption('default', $column);
if ($isNullable === false && $defaultSet !== null) {
return sprintf("DEFAULT '%s'", $defaultSet);
}
return "DEFAULT ''";
}
/**
* @param ColumnInterface $column
*
* @return bool
*/
private function isAutoIncrementField(ColumnInterface $column): bool
{
return $this->hasOption('extra', $column) && in_array(
$this->getOption('extra', $column),
['ai', 'auto_increment', 'AUTO_INCREMENT'],
true
);
}
/**
* @param string $extra
*
* @return string
*/
private function extraMapping(string $extra): string
{
$short = ['ai' => 'AUTO_INCREMENT'];
if (array_key_exists($extra, $short)) {
return $short[$extra];
}
return strtoupper($extra);
}
/**
* @param string $tableName
*
* @throws LineGeneratorException
*
* @return array
*/
public function fetchColumnsFromDb(string $tableName): array
{
try {
$columns = $this->db->fetchAll('SHOW COLUMNS FROM ' . $tableName);
} catch (QueryFailureException $exception) {
throw new LineGeneratorException(
$exception->getMessage(),
$exception->getCode(),
$exception->getPrevious()
);
}
$result = [];
foreach ($columns as $column) {
$isNull = strtoupper($column['Null']) === 'YES' && $column['Default'] === null;
$isNullable = strtoupper($column['Null']) === 'YES';
$extra = !empty($column['Extra']) ? strtoupper($column['Extra']) : null;
$default = $isNull ? 'DEFAULT NULL' : "DEFAULT '" . $column['Default'] . "'";
if ($isNullable === false && $isNull === false) {
$default = "NOT NULL DEFAULT '" . $column['Default'] . "'";
}
$resultType = $column['Type'];
$references = null;
if (preg_match('/^(enum|set)\w*/i', $resultType, $found) && count($found) > 0) {
$resultType = strtoupper($found[0]);
$reference_values = str_replace(['(', ')', $found[0], '\''], '', $column['Type']);
$references = $reference_values;
} else {
$resultType = strtoupper($resultType);
}
$length = null;
$decimals = null;
preg_match('/^(decimal|double|float)\(\d*(,\d*)?\)/i', $resultType, $decimalFound);
if (count($decimalFound) > 0) {
$decimals = 0;
preg_match('/\(\d*(,\d*)?\)/', $resultType, $doubleLengthFound);
if (count($decimalFound) === 3) {
$decimals = (int)str_replace(',', '', $decimalFound[2]);
}
$length = (int)str_replace(['(', ')'], '', $doubleLengthFound[0]);
$resultType = str_replace($decimalFound[0], $decimalFound[1], $resultType);
}
if ($length === null) {
preg_match('/\(\d*\)/', $resultType, $lengthFound);
if (count($lengthFound) > 0) {
$length = (int)str_replace(['(', ')'], '', $lengthFound[0]);
$resultType = str_replace($lengthFound[0], '', $resultType);
}
}
$unsigned = false;
$resultType_exploded = explode(' ', $resultType);
if (count($resultType_exploded) > 1 && in_array(
$resultType_exploded[1],
[NumericTypeInterface::WITH_NEGATIV, NumericTypeInterface::NON_NEGATIV],
true
)) {
$unsigned = $resultType_exploded[1] === NumericTypeInterface::NON_NEGATIV;
$resultType = trim(str_replace($resultType_exploded[1], '', $resultType));
}
$result[] = [
'field' => $column['Field'],
'type' => $resultType,
'length' => $length,
'decimals' => $decimals,
'unsigned' => $unsigned,
'nullable' => $isNullable,
'default' => $column['Default'],
'extra' => $extra,
'references' => $references,
'sql_default' => $extra === 'AUTO_INCREMENT' ? 'NOT NULL' : $default,
];
}
return $result;
}
/**
* @param string $value
*
* @return bool
*/
private function containMySQLTimeConstant(string $value): bool
{
return (boolean)preg_match('/(\bCURRENT_TIMESTAMP\b|\bNOW\b|\bLOCALTIME\b|\bLOCALTIMESTAMP\b)/i', $value);
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\LineGenerator\Common;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\QueryFailureException;
use Xentral\Components\SchemaCreator\Exception\LineGeneratorException;
final class ConstraintLineGenerator
{
/** @var Database $db */
private $db;
/**
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* @param string $tableName
*
* @throws LineGeneratorException
*
* @return array
*/
public function fetchConstraintsFromDb(string $tableName): array
{
try {
$dbName = $this->db->fetchValue('SELECT DATABASE()');
$constraints = $this->db->fetchAll(
sprintf(
"SELECT `COLUMN_NAME`, `REFERENCED_TABLE_NAME`,
`REFERENCED_COLUMN_NAME`, `CONSTRAINT_NAME`
FROM `information_schema`.`key_column_usage`
WHERE `referenced_table_name` IS NOT NULL AND `TABLE_SCHEMA` = '%s' AND `TABLE_NAME` = '%s' ",
$dbName,
$tableName
)
);
} catch (QueryFailureException $exception) {
throw new LineGeneratorException(
$exception->getMessage(), $exception->getCode(), $exception->getPrevious()
);
}
$result = [];
foreach ($constraints as $constraint) {
$constraintName = $constraint['CONSTRAINT_NAME'];
$resultKey = array_search($constraintName, array_column($result, 'name'), true);
if ($resultKey !== false) {
$result[$resultKey]['columns'][] = $constraint['COLUMN_NAME'];
$result[$resultKey]['reference_columns'][] = $constraint['COLUMN_NAME'];
continue;
}
$result[] = [
'name' => $constraint['CONSTRAINT_NAME'],
'reference_table' => $constraint['REFERENCED_TABLE_NAME'],
'reference_columns' => [$constraint['REFERENCED_COLUMN_NAME']],
'columns' => [$constraint['COLUMN_NAME']],
];
}
return $result;
}
}
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\LineGenerator\Common;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\Database\Exception\QueryFailureException;
use Xentral\Components\SchemaCreator\Exception\LineGeneratorException;
use Xentral\Components\SchemaCreator\Index\Constraint;
use Xentral\Components\SchemaCreator\Interfaces\PrimaryKeyInterface;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
final class IndexLineGenerator
{
/** @var Database $db */
private $db;
/**
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* @param string $indexName
*
* @throws EscapingException
*
* @return string
*/
private function generateName(string $indexName): string
{
if (!empty($indexName)) {
$indexName = $this->escape($indexName);
}
return $indexName;
}
/**
* @param $creatorIndex
*
* @throws EscapingException
*
* @return string
*/
private function buildReferences($creatorIndex): string
{
if ($creatorIndex instanceof Constraint) {
$default = ['delete'];
$asCascade = $creatorIndex->getCascadeOn();
if (empty($asCascade)) {
$asCascade = $default;
}
$cascade = '';
foreach ($asCascade as $cascadeCase) {
$cascade .= sprintf(' ON %s CASCADE ', strtoupper($cascadeCase));
}
return sprintf(
'FOREIGN KEY (%s) REFERENCES %s (%s)%s',
implode(',', array_map([$this, 'escape'],$creatorIndex->getForeignKey())),
$this->escape($creatorIndex->getParentTable()),
implode(',', array_map([$this, 'escape'], $creatorIndex->getParenId())),
$cascade
);
}
$reference = implode(
',',
array_map(
function ($reference) {
return $this->escape($reference);
},
$creatorIndex->getReferences()
)
);
return sprintf('(%s)', $reference);
}
/**
* @param IndexInterface $creatorIndex
*
* @throws EscapingException
*
* @return string
*/
public function generateLine(IndexInterface $creatorIndex): string
{
$line = $creatorIndex->getType();
if (!($creatorIndex instanceof PrimaryKeyInterface)) {
$lineName = $this->generateName($creatorIndex->getName());
$line .= ' ' . $lineName;
}
$line .= ' ' . $this->buildReferences($creatorIndex);
return trim($line);
}
/**
* @param IndexInterface $creatorIndex
*
* @return string
*/
private function getName(IndexInterface $creatorIndex): string
{
return $creatorIndex->getName() ?? '';
}
/**
* @param IndexInterface $creatorIndex
*
* @return string
*/
public function getKeyName(IndexInterface $creatorIndex): string
{
if ($creatorIndex instanceof PrimaryKeyInterface) {
return 'PRIMARY';
}
$keyName = $this->getName($creatorIndex);
return $keyName ?? trim(str_replace('KEY', '', $creatorIndex->getType()));
}
/**
* @param string $tableName
*
* @throws LineGeneratorException
*
* @return array
*/
public function fetchIndexesFromDb(string $tableName): array
{
try {
$indexes = $this->db->fetchAll('SHOW INDEXES FROM ' . $this->db->escapeIdentifier($tableName));
} catch (QueryFailureException | EscapingException $exception) {
throw new LineGeneratorException(
$exception->getMessage(),
$exception->getCode(),
$exception->getPrevious()
);
}
$result = [];
foreach ($indexes as $index) {
$keyName = $index['Key_name'];
$resultKey = array_search($keyName, array_column($result, 'name'), true);
if ($resultKey !== false) {
$result[$resultKey]['columns'][] = $index['Column_name'];
}
if ($resultKey === false) {
$result[] = [
'name' => $index['Key_name'],
'columns' => [$index['Column_name']],
'unique' => (int)$index['Non_unique'] === 0,
];
}
}
return $result;
}
/**
* @param string $name
*
* @throws EscapingException
*
* @return string
*/
public function escape(string $name): string
{
return $this->db->escapeIdentifier($name);
}
}
@@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\LineGenerator\Common;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\SchemaCreator\Option\TableOption;
final class TableOptionsGenerator
{
/** @var Database */
private $db;
/** @var string $optionLine */
private $optionLine;
/** @var string[] $defaultParams */
protected $defaultParams = [
'engine' => 'InnoDB',
'table_charset' => 'utf8',
'collation' => 'utf8_general_ci',
'comment' => null,
];
/**
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* @param array $options
*
* @throws EscapingException
*
* @return string
*/
public function generate(array $options = []): string
{
$options = array_merge($this->defaultParams, $options);
$this->addEngine($options['engine']);
if (array_key_exists('table_charset', $options) && !empty($options['table_charset'])) {
$this->addCharset($options['table_charset']);
}
if (array_key_exists('collation', $options) && !empty($options['collation'])) {
$this->addCollation($options['collation']);
}
if (array_key_exists('comment', $options) && !empty($options['comment'])) {
$this->addComment($options['comment']);
}
return $this->optionLine;
}
/**
* @param string $engine
*
* @throws EscapingException
*
* @return void
*/
private function addEngine(string $engine): void
{
$this->optionLine = sprintf(' ENGINE=%s', $this->db->escapeString($engine));
}
/**
* @param string $charset
*
* @throws EscapingException
*
* @return void
*/
private function addCharset(string $charset): void
{
$this->optionLine .= sprintf(' DEFAULT CHARSET=%s', $this->db->escapeString($charset));
}
/**
* @param string $collation
*
* @throws EscapingException
*
* @return void
*/
private function addCollation(string $collation): void
{
$this->optionLine .= sprintf(' DEFAULT COLLATE = %s', $this->db->escapeString($collation));
}
/**
* @param string $comment
*
* @throws EscapingException
*
* @return void
*/
private function addComment(string $comment): void
{
$this->optionLine .= sprintf(" COMMENT = %s", $this->db->escapeString($comment));
}
/**
* @param string $tableName
*
* @return array
*/
public function fetchOptionsFromDb(string $tableName): array
{
$options = $this->db->fetchRow('SHOW TABLE STATUS WHERE Name =:table', ['table' => $tableName]);
return [
'engine' => array_key_exists('Engine', $options) ? $options['Engine'] : 'InnoDB',
'table_charset' => array_key_exists('Charset', $options) ? $options['Charset'] : 'utf8',
'collation' => array_key_exists('Collation', $options) ? $options['Collation'] : 'utf8_general_ci',
'comment' => array_key_exists('Comment', $options) ? $options['Comment'] : '',
];
}
/**
* @param TableOption $option
* @param string|null $charset
*
* @throws EscapingException
*
* @return string
*/
public function alterOptions(TableOption $option, ?string $charset = null): string
{
switch ($option->getOption()) {
case 'comment':
$sqlOption = sprintf('COMMENT = %s', $this->db->escapeString($option->getValue()));
break;
case 'engine':
$sqlOption = sprintf('ENGINE = %s', $this->db->escapeString($option->getValue()));
break;
case 'charset':
$sqlOption = sprintf('CONVERT TO CHARACTER SET %s', $this->db->escapeString($option->getValue()));
break;
case 'collation':
$charset = $charset ?? $this->defaultParams['table_charset'];
$sqlOption = sprintf(
'CONVERT TO CHARACTER SET %s COLLATE %s',
$this->db->escapeString($charset),
$this->db->escapeString($option->getValue())
);
break;
default:
$sqlOption = '';
}
return $sqlOption;
}
}
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Option;
final class TableOption
{
/** @var string $option */
private $option;
/** @var string $value */
private $value;
/**
* @param string $option
* @param string $value
*/
public function __construct(string $option, string $value)
{
$this->option = $option;
$this->value = $value;
}
/**
* @return string
*/
public function getOption(): string
{
return $this->option;
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $engine
*
* @return TableOption
*/
public static function fromEngine(string $engine): TableOption
{
return new self('engine', $engine);
}
/**
* @param string $collation
*
* @return TableOption
*/
public static function fromCollation(string $collation): TableOption
{
return new self('collation', $collation);
}
/**
* @param string $charset
*
* @return TableOption
*/
public static function fromCharset(string $charset): TableOption
{
return new self('charset', $charset);
}
/**
* @param string $comment
*
* @return TableOption
*/
public static function fromComment(string $comment): TableOption
{
return new self('comment', $comment);
}
}
@@ -0,0 +1,176 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Schema;
use Xentral\Components\SchemaCreator\Collection\ColumnCollection;
use Xentral\Components\SchemaCreator\Collection\IndexCollection;
use Xentral\Components\SchemaCreator\Collection\TableOptionCollection;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorTableException;
use Xentral\Components\SchemaCreator\Interfaces\IndexInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Option\TableOption;
final class TableSchema
{
/** @var string $table */
private $table;
/** @var ColumnCollection $columnCollection */
private $columnCollection;
/** @var IndexCollection $indexCollection */
private $indexCollection;
/** @var TableOptionCollection $tableOptionCollection */
private $tableOptionCollection;
/**
* @param string $tableName
* @param ColumnCollection|null $columnCollection
* @param IndexCollection|null $indexCollection
* @param TableOptionCollection|null $tableOptionCollection
*
* @throws SchemaCreatorTableException
*/
public function __construct(
string $tableName,
?ColumnCollection $columnCollection = null,
?IndexCollection $indexCollection = null,
?TableOptionCollection $tableOptionCollection = null
) {
$this->table = trim($tableName);
if (empty($this->table)) {
throw new SchemaCreatorTableException('Table cannot be empty');
}
$this->columnCollection = $columnCollection ?? new ColumnCollection();
$this->indexCollection = $indexCollection ?? new IndexCollection();
$this->tableOptionCollection = $tableOptionCollection ?? new TableOptionCollection();
}
/**
* @param ColumnInterface $column
*
* @return void
*/
public function addColumn(ColumnInterface $column): void
{
$this->columnCollection->add($column);
}
/**
* @param IndexInterface $index
*
* @return void
*/
public function addIndex(IndexInterface $index): void
{
$this->indexCollection->add($index);
}
/**
* @return ColumnCollection|ColumnInterface[]
*/
public function getColumns(): ColumnCollection
{
return $this->columnCollection;
}
/**
* @return IndexCollection|IndexInterface[]
*/
public function getIndexes(): IndexCollection
{
return $this->indexCollection;
}
/**
* @return string
*/
public function getTable(): string
{
return $this->table;
}
/**
* @param string $column
*
* @return bool
*/
public function hasColumn(string $column): bool
{
return $this->columnCollection->hasColumn($column);
}
/**
* @param string $indexName
*
* @return bool
*/
public function hasIndex(string $indexName): bool
{
return $this->indexCollection->hasIndex($indexName);
}
/**
* @param TableOption $option
*/
public function addOption(TableOption $option): void
{
$this->tableOptionCollection->add($option);
}
/**
* @return TableOptionCollection|TableOption[]
*/
public function getOptions(): TableOptionCollection
{
return $this->tableOptionCollection;
}
/**
* @param string $optionName
*
* @return bool
*/
public function hasOption(string $optionName): bool
{
return $this->tableOptionCollection->hasOption($optionName);
}
/**
* @param string $field
*
* @return ColumnInterface|null
*/
public function getColumnByName(string $field): ?ColumnInterface
{
foreach ($this->getColumns() as $configuredColumn) {
if ($field === $configuredColumn->getField()) {
return $configuredColumn;
}
}
return null;
}
/**
* @param string $index
*
* @return IndexInterface|null
*/
public function getIndexByName(string $index): ?IndexInterface
{
foreach ($this->getIndexes() as $configuredIndex) {
if ($index === $configuredIndex->getName()) {
return $configuredIndex;
}
}
return null;
}
}
@@ -0,0 +1,165 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\DatabaseExceptionInterface;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Components\Database\Exception\TransactionException;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorTableException;
use Xentral\Components\SchemaCreator\Interfaces\DriverInterface;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
final class SchemaCreator
{
/** @var Database $db */
private $db;
/** @var DriverInterface $driver */
private $driver;
/**
* @param Database $db
* @param DriverInterface $driver
*/
public function __construct(Database $db, DriverInterface $driver)
{
$this->db = $db;
$this->driver = $driver;
}
/**
* @param TableSchema $targetSchema
*
* @throws EscapingException
* @throws Exception\LineGeneratorException
* @throws SchemaCreatorTableException
* @throws TransactionException
*
* @return void
*/
public function ensureSchema(TableSchema $targetSchema): void
{
$this->applyTableSchema($targetSchema);
}
/**
* @param TableSchema $currentSchema
* @param TableSchema $targetSchema
*
* @throws EscapingException
*
* @return string
*/
public function getDiffSQL(TableSchema $currentSchema, TableSchema $targetSchema): string
{
return $this->driver->generateTableSchemaDiff($currentSchema, $targetSchema);
}
/**
* @param string $table
*
* @throws Exception\LineGeneratorException
* @throws SchemaCreatorTableException
*
* @return TableSchema
*/
public function createFromExistingTable(string $table): TableSchema
{
return $this->driver->loadFromTable($table);
}
/**
* @param TableSchema $targetSchema
*
* @throws EscapingException
* @throws Exception\LineGeneratorException
* @throws SchemaCreatorTableException
* @throws TransactionException
*
* @return void
*/
private function applyTableSchema(TableSchema $targetSchema): void
{
$sqlSchema = $this->getSqlSchema($targetSchema);
$this->applySqlSchema($sqlSchema);
}
/**
* Check whether the table exists
*
* @param string $tableName
*
* @return bool
*/
private function hasTable(string $tableName): bool
{
$tables = $this->db->fetchCol('SHOW TABLES');
return in_array($tableName, $tables, true);
}
/**
* @param TableSchema $schema
*
* @throws EscapingException
*
* @return string
*/
private function generateSQLDefinition(TableSchema $schema): string
{
return $this->driver->getTableDefinition($schema);
}
/**
* @param TableSchema $schema
*
* @throws EscapingException
* @throws Exception\LineGeneratorException
* @throws SchemaCreatorTableException
*
* @return string
*/
public function getSqlSchema(TableSchema $schema): string
{
$table = $schema->getTable();
if (!$this->hasTable($table)) {
return $this->generateSQLDefinition($schema);
}
$currentSchema = $this->createFromExistingTable($table);
$diffSQL = $this->getDiffSQL($currentSchema, $schema);
if (empty($diffSQL)) {
return '';
}
return $diffSQL;
}
/**
* @param string $sql
*
* @throws SchemaCreatorTableException
* @throws TransactionException
*
* @return void
*/
private function applySqlSchema(string $sql): void
{
if (empty($sql)) {
return;
}
$this->db->beginTransaction();
try {
$this->db->exec($sql);
$this->db->commit();
} catch (DatabaseExceptionInterface $e) {
$this->db->rollBack();
throw new SchemaCreatorTableException($e->getMessage(), (int)$e->getCode(), $e);
}
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Bigint implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'BIGINT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 20;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @param array $options
*
* @return IntegerTypeInterface
* @internal Use constructor instead
*
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\CharTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Binary implements ColumnInterface, CharTypeInterface
{
/** @var string $type */
private $type = 'BINARY';
/** @var int $length */
private $length;
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 32;
/**
* @param string $name
* @param int $length
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->length = $length;
$this->options = $options;
$this->name = $name;
$this->nullable = $nullable;
$this->options['default'] = $default;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Bit implements ColumnInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'BIT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 6;
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @param array $options
*
* @return Bit
*
* @internal Use constructor instead
*
*/
public static function fromDBColumn(array $options): Bit
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Blob implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'BLOB';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->options = $options;
$this->nullable = $nullable;
$this->options['default'] = null;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\CharTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Char implements ColumnInterface, CharTypeInterface
{
/** @var string $type */
private $type = 'CHAR';
/** @var int $length */
private $length;
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 32;
/**
* @param string $name
* @param int $length
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->length = $length;
$this->options = $options;
$this->name = $name;
$this->nullable = $nullable;
$this->options['default'] = $default;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DateAndTimeColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Date implements ColumnInterface, DateAndTimeColumnInterface
{
/** @var string $type */
private $type = 'DATE';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DateAndTimeColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Datetime implements ColumnInterface, DateAndTimeColumnInterface
{
/** @var string $type */
private $type = 'DATETIME';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$options['default'] = $default;
$this->nullable = $nullable;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,197 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DecimalTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Decimal
implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface, DecimalTypeInterface
{
/** @var string $type */
private $type = 'DECIMAL';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var int $decimals */
private $decimals;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 10;
/**
* Decimal constructor.
*
* @param string $name
* @param int $length
* @param int $decimals
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
int $decimals = 0,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->decimals = $decimals;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
$this->isUnsigned = $unsigned;
}
/**
* @param array $options
*
* @return DecimalTypeInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): DecimalTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
if ($options['decimals'] === null) {
$options['decimals'] = 0;
}
return new self(
$options['field'],
$options['length'],
$options['decimals'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param int $decimals
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
int $decimals = 0,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $decimals, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int $decimals
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
int $decimals = 0,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, $decimals, true, $default, $nullable, $options);
}
/**
* @inheritDoc
*/
public function getDecimals(): int
{
return $this->decimals;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Double implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'DOUBLE';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 7;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @param array $options
*
* @return IntegerTypeInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\EnumAndSetInterface;
final class Enum implements ColumnInterface, EnumAndSetInterface
{
/** @var string $type */
private $type = 'ENUM';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var array $references */
private $references;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param array $references
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
array $references,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->references = $references;
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
if (null !== $default && !in_array($default, $references, true)) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Default value %s not found in References', $default)
);
}
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['references'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getReferences(): string
{
$formattedReferences = array_map(
static function ($value) {
return sprintf("'%s'", $value);
},
$this->references
);
return implode(',', $formattedReferences);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Integer implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'INT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 10;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return Integer
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return Integer
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @param array $options
*
* @return IntegerTypeInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Json implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'JSON';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name,bool $nullable = true, array $options = [])
{
$this->nullable = $nullable;
$options['default'] = null;
$this->name = $name;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Longblob implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'LONGBLOB';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Longtext implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'LONGTEXT';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Mediumblob implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'MEDIUMBLOB';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Mediumint implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'MEDIUMINT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 9;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*
* @internal Use constructor instead
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Mediumtext implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'MEDIUMTEXT';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\EnumAndSetInterface;
final class Set implements ColumnInterface, EnumAndSetInterface
{
/** @var string $type */
private $type = "SET";
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var array $references */
private $references;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param array $references
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
array $references,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->references = $references;
$this->options = $options;
$this->name = $name;
$this->nullable = $nullable;
$this->options['default'] = $default;
if (null !== $default && !in_array($default, $references, true)) {
throw new SchemaCreatorInvalidArgumentException(
sprintf('Default value %s not found in References', $default)
);
}
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['references'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getReferences(): string
{
$formattedReferences = array_map(
static function ($value) {
return sprintf("'%s'", $value);
},
$this->references
);
return implode(',', $formattedReferences);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Smallint implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'SMALLINT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 6;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @param array $options
*
* @return IntegerTypeInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,89 @@
<?php
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Text implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'TEXT';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DateAndTimeColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Time implements ColumnInterface, DateAndTimeColumnInterface
{
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var string $type */
private $type = 'TIME';
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DateAndTimeColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Timestamp implements ColumnInterface, DateAndTimeColumnInterface
{
/** @var string $type */
private $type = 'TIMESTAMP';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Tinyblob implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'TINYBLOB';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(string $name, bool $nullable = true, array $options = [])
{
$this->name = $name;
$this->nullable = $nullable;
$options['default'] = null;
$this->options = $options;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\IntegerTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\NumericTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Tinyint implements ColumnInterface, NumericTypeInterface, IntegerTypeInterface
{
/** @var string $type */
private $type = 'TINYINT';
/** @var int $length */
private $length;
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $isUnsigned */
private $isUnsigned;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 4;
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param int|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = false,
?int $default = null,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->length = $length;
$this->isUnsigned = $unsigned;
$this->nullable = $nullable;
$options['default'] = $default;
$this->options = $options;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isUnsigned(): bool
{
return $this->isUnsigned;
}
/**
* @param string $name
* @param int $length
* @param bool $unsigned
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asAutoIncrement(
string $name,
int $length = self::DEFAULT_LENGTH,
bool $unsigned = true,
array $options = []
): IntegerTypeInterface {
$options['extra'] = 'ai';
return new self($name, $length, $unsigned, null, false, $options);
}
/**
* @param string $name
* @param int $length
* @param int|null $default
* @param bool $nullable
* @param array $options
*
* @return IntegerTypeInterface
*/
public static function asUnsigned(
string $name,
int $length = self::DEFAULT_LENGTH,
?int $default = null,
bool $nullable = true,
array $options = []
): IntegerTypeInterface {
return new self($name, $length, true, $default, $nullable, $options);
}
/**
* @param array $options
*
* @return IntegerTypeInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): IntegerTypeInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['unsigned'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnTextInterface;
final class Tinytext implements ColumnInterface, ColumnTextInterface
{
/** @var string $type */
private $type = 'TINYTEXT';
/** @var string $name */
private $name;
/** @var array $options */
private $options;
/** @var bool $nullable */
private $nullable;
/**
* @param string $name
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
bool $nullable = true,
array $options = []
) {
$this->name = $name;
$this->options = $options;
$this->nullable = $nullable;
$this->options['default'] = null;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
return new self(
$options['field'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\CharTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Varbinary implements ColumnInterface, CharTypeInterface
{
/** @var string $type */
private $type = 'VARBINARY';
/** @var int $length */
private $length;
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 32;
/**
* @param string $name
* @param int $length
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->length = $length;
$this->options = $options;
$this->name = $name;
$this->nullable = $nullable;
$this->options['default'] = $default;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\CharTypeInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Varchar implements ColumnInterface, CharTypeInterface
{
/** @var string $type */
private $type = "VARCHAR";
/** @var int $length */
private $length;
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 128;
/**
* @param string $name
* @param int $length
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?string $default = null,
bool $nullable = true,
array $options = []
) {
$this->length = $length;
$this->options = $options;
$this->name = $name;
$this->nullable = $nullable;
$this->options['default'] = $default;
}
/**
* @inheritDoc
*/
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Exception\SchemaCreatorInvalidArgumentException;
use Xentral\Components\SchemaCreator\Interfaces\DateAndTimeColumnInterface;
use Xentral\Components\SchemaCreator\Interfaces\ColumnInterface;
final class Year implements ColumnInterface, DateAndTimeColumnInterface
{
/** @var string $type */
private $type = 'YEAR';
/** @var array $options */
private $options;
/** @var string $name */
private $name;
/** @var int $length */
private $length;
/** @var bool $nullable */
private $nullable;
/** @var int */
private const DEFAULT_LENGTH = 4;
/**
* @param string $name
* @param int $length
* @param string|null $default
* @param bool $nullable
* @param array $options
*/
public function __construct(
string $name,
int $length = self::DEFAULT_LENGTH,
?string $default = null,
bool $nullable = true,
array $options = []
) {
{
$this->length = $length;
$this->name = $name;
$this->options = $options;
$this->nullable = $nullable;
$this->options['default'] = $default;
}
}
/**
* @param array $options
*
* @return ColumnInterface
*
* @internal Use constructor instead
*/
public static function fromDBColumn(array $options): ColumnInterface
{
if (empty($options)) {
throw new SchemaCreatorInvalidArgumentException('Options cannot be empty');
}
if ($options['length'] === null) {
$options['length'] = self::DEFAULT_LENGTH;
}
return new self(
$options['field'],
$options['length'],
$options['default'],
$options['nullable'],
$options
);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return $this->type;
}
/**
* @inheritDoc
*/
public function getField(): string
{
return $this->name;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
public function getLength(): int
{
return $this->length;
}
/**
* @inheritDoc
*/
public function isNullable(): bool
{
return $this->nullable;
}
}
@@ -0,0 +1,135 @@
# Synopsis
* Create defined Table Schema if it does not exist
* Ensure that existing table, and the target schema are the same
# Usage
Example:
```php
use Xentral\Components\SchemaCreator\Option\TableOption;
use Xentral\Components\SchemaCreator\SchemaCreator;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
use Xentral\Components\SchemaCreator\Type;
/** @var SchemaCreator $tableCreator */
$tableCreator = $this->app->Container->get('SchemaCreator');
$fooTable = new TableSchema('foo_table');
$fooTable->addColumn(Type\Integer::asAutoIncrement('id'));
$fooTable->addColumn(Type\Integer::asUnsigned('user_id'));
$fooTable->addColumn(new Type\Varchar('name'));
$fooTable->addColumn(new Type\Varchar('language', 5));
$fooTable->addColumn(new Type\Varchar('first_name', 100));
$fooTable->addColumn(new Type\Varchar('last_name', 200, '', false));
$fooTable->addColumn(new Type\Tinyint('active',1,false));
$fooTable->addColumn(new Type\Time('timed_at','00:00:00'));
$fooTable->addColumn(new Type\Year('year_example'));
$fooTable->addOption(TableOption::fromEngine('InnoDB'));
$fooTable->addOption(TableOption::fromCharset('utf8'));
$fooTable->addOption(TableOption::fromComment('This is a comment'));
$fooTable->addOption(TableOption::fromCollation('utf8_unicode_ci'));
$tableCreator->ensureSchema($fooTable);
// display table's structure
var_export($tableCreator->getSqlSchema($fooTable));
```
Example with Index
```php
use Xentral\Components\SchemaCreator\SchemaCreator;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
use Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Index;
/** @var SchemaCreator $tableCreator */
$tableCreator = $this->app->Container->get('SchemaCreator');
$barTable = new TableSchema('bar_table');
$barTable->addColumn(new Type\Integer('id',10, true,null,false, ['extra' => 'ai']));
$barTable->addColumn(new Type\Integer('foo_table_id',10, true));
$barTable->addColumn(new Type\Varchar('langue'));
$barTable->addColumn(new Type\Varchar('prenom'));
$barTable->addColumn(new Type\Varchar('nom_de_famille'));
$barTable->addColumn(new Type\Tinyint('actif', 1));
$barTable->addIndex(new Index\Primary(['id']));
$barTable->addIndex(new Index\Unique(['prenom'], 'unique_first_name'));
$barTable->addIndex(new Index\Unique(['langue', 'actif']));
$barTable->addIndex(new Index\Index(['nom_de_famille'], 'custom_name'));
$tableCreator->ensureSchema($barTable);
```
Generate Schema from existing table
```php
use Xentral\Components\SchemaCreator\SchemaCreator;
/** @var SchemaCreator $tableCreator */
$tableCreator = $this->app->Container->get('SchemaCreator');
$currentSchema = $tableCreator->createFromExistingTable('foo_table');
// get table's definition
$tableDefinition = $tableCreator->getSqlSchema($currentSchema);
var_export($tableDefinition);
```
# Defines Table Schema in your module
```php
declare(strict_types=1);
namespace Xentral\Modules\Foo;
use Xentral\Components\SchemaCreator\Collection\SchemaCollection;
use Xentral\Components\SchemaCreator\Schema\TableSchema;
use Xentral\Components\SchemaCreator\Type;
use Xentral\Components\SchemaCreator\Index;
use Xentral\Components\SchemaCreator\Option\TableOption;
final class Bootstrap
{
/**
* @param SchemaCollection $collection
*
* @return void
*/
public static function registerTableSchemas(SchemaCollection $collection): void
{
$fooTable = new TableSchema('foo_table');
$fooTable->addColumn(Type\Integer::asAutoIncrement('id'));
$fooTable->addColumn(Type\Integer::asUnsigned('user_id'));
$fooTable->addColumn(new Type\Varchar('name'));
$fooTable->addColumn(new Type\Varchar('language', 5));
$fooTable->addColumn(new Type\Varchar('first_name', 100));
$fooTable->addColumn(new Type\Varchar('last_name', 200, '', false));
$fooTable->addColumn(new Type\Tinyint('active',1,false));
$fooTable->addColumn(new Type\Time('timed_at',null, false));
$fooTable->addColumn(new Type\Year('year_example'));
$fooTable->addOption(TableOption::fromEngine('InnoDB'));
$fooTable->addOption(TableOption::fromCharset('utf8'));
$fooTable->addOption(TableOption::fromComment('This is a comment'));
$fooTable->addOption(TableOption::fromCollation('utf8_unicode_ci'));
$barTable = new TableSchema('bar_table');
$barTable->addColumn(new Type\Integer('id',10, true,null,false, ['extra' => 'ai']));
$barTable->addColumn(new Type\Integer('foo_table_id',10, true));
$barTable->addColumn(new Type\Varchar('langue'));
$barTable->addColumn(new Type\Varchar('prenom'));
$barTable->addColumn(new Type\Varchar('nom_de_famille'));
$barTable->addColumn(new Type\Tinyint('actif', 1));
$barTable->addIndex(new Index\Primary(['id']));
$barTable->addIndex(new Index\Unique(['prenom'], 'unique_first_name'));
$barTable->addIndex(new Index\Unique(['langue', 'actif']));
$barTable->addIndex(new Index\Index(['nom_de_famille'], 'custom_name'));
// ADD TableSchema
$collection->add($fooTable);
$collection->add($barTable);
}
# ...
}
```