WP-Database-Helper/Migration.php
2025-02-24 10:54:51 +01:00

160 lines
5.2 KiB
PHP

<?php
namespace DatabaseHelper;
use DatabaseHelper\beans\Column;
use DatabaseHelper\beans\Reference;
use DatabaseHelper\beans\Schema;
use DatabaseHelper\enums\Propagation;
use DatabaseHelper\enums\Type;
use InvalidArgumentException;
class Migration
{
protected Schema $schema;
protected array $newCols = [];
protected array $editCols = [];
protected array $dropCols = [];
protected array $addRefs = [];
protected array $dropRefs = [];
public function __construct(Schema $table) {
$this->schema = $table;
}
public function column(string $name, Type $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Migration {
$table = $this->schema->name;
if ($this->schema->existsCol($name))
throw new InvalidArgumentException("Column '$name' already exists in $table.");
$col = new Column;
$col->name = $name;
$col->type = $type;
$col->default = $default;
$col->isUnique = $isUnique;
$col->isNullable = $isNullable;
$this->schema->cols[$name] = $col;
$this->newCols[] = $name;
return $this;
}
public function modify(string $name, Type $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Migration {
$this->schema->reqCol($name);
$col = $this->schema->getCol($name);
$col->name = $name;
$col->type = $type;
$col->default = $default;
$col->isNullable = $isNullable;
$col->isUnique = $isUnique;#
$this->editCols[$name] = $col;
return $this;
}
public function drop(string $name): Migration {
unset($this->schema->cols[$name]);
$this->dropCols[] = $name;
return $this;
}
public function reference(Schema $foreignTable, Propagation $onDelete = Propagation::CASCADE, Propagation $onUpdate = Propagation::CASCADE): Migration {
if($this->schema->existsRef($foreignTable))
throw new InvalidArgumentException("Foreign table '$foreignTable->name' is already referenced in '{$this->schema->name}'.");
$name = $foreignTable->id->name;
$ref = new Reference;
$ref->name = $name;
$ref->onUpdate = $onUpdate;
$ref->onDelete = $onDelete;
$this->schema->refs[$name] = $ref;
$this->addRefs[] = $name;
return $this;
}
public function dereference(Schema $foreignTable): Migration {
$this->schema->reqRef($foreignTable);
$name = $foreignTable->id->name;
unset($this->schema->refs[$name]);
$this->dropRefs[] = $name;
return $this;
}
public function toSql(): string {
global $wpdb;
// We assume that the table name in the schema is not prefixed; add the prefix here.
$tableName = $wpdb->prefix . $this->schema->name;
$clauses = [];
// Process new columns.
foreach ($this->newCols as $columnName) {
$col = $this->schema->cols[$columnName];
$clause = "ADD COLUMN `{$col['name']}` {$col['type']->toString()}";
$clause .= !$col['isNullable'] ? " NOT NULL" : "";
$clause .= $col['isUnique'] ? " UNIQUE" : "";
if (!is_null($col['default'])) {
$default = is_string($col['default']) ? "'{$col['default']}'" : $col['default'];
$clause .= " DEFAULT $default";
}
$clauses[] = $clause;
}
// Process modified columns.
foreach ($this->editCols as $columnName) {
$col = $this->schema->cols[$columnName];
$clause = "MODIFY COLUMN `{$col['name']}` {$col['type']->toString()}";
$clause .= !$col['isNullable'] ? " NOT NULL" : "";
$clause .= $col['isUnique'] ? " UNIQUE" : "";
if (!is_null($col['default'])) {
$default = is_string($col['default']) ? "'{$col['default']}'" : $col['default'];
$clause .= " DEFAULT $default";
}
$clauses[] = $clause;
}
// Process dropped columns.
foreach ($this->dropCols as $columnName)
$clauses[] = "DROP COLUMN `$columnName`";
// Process foreign keys to add.
foreach ($this->addRefs as $key) {
$foreignKey = $this->schema->refs[$key];
$clause = " ADD CONSTRAINT `fk_{$foreignKey['name']}`";
$clause .= " FOREIGN KEY (`{$foreignKey['name']}`)";
$clause .= " REFERENCES `{$foreignKey['table']}` (`{$foreignKey['name']}`)";
$clause .= " ON DELETE {$foreignKey['onDelete']->toString()}";
$clause .= " ON UPDATE {$foreignKey['onUpdate']->toString()},\n";
$clauses[] = $clause;
}
// Process foreign keys to drop.
foreach ($this->dropRefs as $fkName)
$clauses[] = "DROP FOREIGN KEY `fk_{$fkName}`";
if (empty($clauses))
throw new InvalidArgumentException("No migration operations to perform.");
return "ALTER TABLE `$tableName`\n" . implode(",\n", $clauses) . ";";
}
public function migrate(): Schema {
global $wpdb;
$sql = $this->toSql();
$wpdb->query($sql);
return $this->schema;
}
}