Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -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);
|
||||
}
|
||||
# ...
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user