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; } }