415 lines
8.5 KiB
PHP
415 lines
8.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Core\Database;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* NovaCore Database Blueprint
|
||
|
|
* Schema builder for migrations
|
||
|
|
*/
|
||
|
|
class Blueprint
|
||
|
|
{
|
||
|
|
private string $table;
|
||
|
|
private array $columns = [];
|
||
|
|
private array $indexes = [];
|
||
|
|
private string $primaryKey = 'id';
|
||
|
|
private array $timestamps = [];
|
||
|
|
|
||
|
|
public function __construct(string $table)
|
||
|
|
{
|
||
|
|
$this->table = $table;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add primary key
|
||
|
|
*/
|
||
|
|
public function id(string $column = 'id'): Column
|
||
|
|
{
|
||
|
|
$this->primaryKey = $column;
|
||
|
|
$column = new Column('id', 'INT AUTO_INCREMENT PRIMARY KEY');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add string column
|
||
|
|
*/
|
||
|
|
public function string(string $name, int $length = 255): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, "VARCHAR({$length})");
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add text column
|
||
|
|
*/
|
||
|
|
public function text(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'TEXT');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add long text column
|
||
|
|
*/
|
||
|
|
public function longText(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'LONGTEXT');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add integer column
|
||
|
|
*/
|
||
|
|
public function integer(string $name, int $length = null): Column
|
||
|
|
{
|
||
|
|
$type = $length ? "INT({$length})" : 'INT';
|
||
|
|
$column = new Column($name, $type);
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add big integer column
|
||
|
|
*/
|
||
|
|
public function bigInteger(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'BIGINT');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add small integer column
|
||
|
|
*/
|
||
|
|
public function smallInteger(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'SMALLINT');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add tiny integer column
|
||
|
|
*/
|
||
|
|
public function tinyInteger(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'TINYINT');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add boolean column
|
||
|
|
*/
|
||
|
|
public function boolean(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'BOOLEAN');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add decimal column
|
||
|
|
*/
|
||
|
|
public function decimal(string $name, int $precision = 8, int $scale = 2): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, "DECIMAL({$precision}, {$scale})");
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add float column
|
||
|
|
*/
|
||
|
|
public function float(string $name, int $precision = 8, int $scale = 2): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, "FLOAT({$precision}, {$scale})");
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add double column
|
||
|
|
*/
|
||
|
|
public function double(string $name, int $precision = 8, int $scale = 2): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, "DOUBLE({$precision}, {$scale})");
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add date column
|
||
|
|
*/
|
||
|
|
public function date(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'DATE');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add datetime column
|
||
|
|
*/
|
||
|
|
public function datetime(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'DATETIME');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add timestamp column
|
||
|
|
*/
|
||
|
|
public function timestamp(string $name): Column
|
||
|
|
{
|
||
|
|
$column = new Column($name, 'TIMESTAMP');
|
||
|
|
$this->columns[] = $column;
|
||
|
|
return $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add timestamps
|
||
|
|
*/
|
||
|
|
public function timestamps(): void
|
||
|
|
{
|
||
|
|
$this->timestamp('created_at')->nullable();
|
||
|
|
$this->timestamp('updated_at')->nullable();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add soft deletes
|
||
|
|
*/
|
||
|
|
public function softDeletes(): void
|
||
|
|
{
|
||
|
|
$this->timestamp('deleted_at')->nullable();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add index
|
||
|
|
*/
|
||
|
|
public function index(array $columns, string $name = null): void
|
||
|
|
{
|
||
|
|
if (!$name) {
|
||
|
|
$name = $this->table . '_' . implode('_', $columns) . '_index';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->indexes[] = [
|
||
|
|
'name' => $name,
|
||
|
|
'columns' => $columns,
|
||
|
|
'type' => 'INDEX'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add unique index
|
||
|
|
*/
|
||
|
|
public function unique(array $columns, string $name = null): void
|
||
|
|
{
|
||
|
|
if (!$name) {
|
||
|
|
$name = $this->table . '_' . implode('_', $columns) . '_unique';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->indexes[] = [
|
||
|
|
'name' => $name,
|
||
|
|
'columns' => $columns,
|
||
|
|
'type' => 'UNIQUE'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add foreign key
|
||
|
|
*/
|
||
|
|
public function foreign(string $column): ForeignKey
|
||
|
|
{
|
||
|
|
$foreignKey = new ForeignKey($column);
|
||
|
|
$this->columns[] = $foreignKey;
|
||
|
|
return $foreignKey;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate SQL
|
||
|
|
*/
|
||
|
|
public function toSql(): string
|
||
|
|
{
|
||
|
|
$sql = "CREATE TABLE `{$this->table}` (";
|
||
|
|
|
||
|
|
$columnDefinitions = [];
|
||
|
|
foreach ($this->columns as $column) {
|
||
|
|
$columnDefinitions[] = $column->toSql();
|
||
|
|
}
|
||
|
|
|
||
|
|
$sql .= implode(', ', $columnDefinitions);
|
||
|
|
$sql .= ")";
|
||
|
|
|
||
|
|
return $sql;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Column definition
|
||
|
|
*/
|
||
|
|
class Column
|
||
|
|
{
|
||
|
|
private string $name;
|
||
|
|
private string $type;
|
||
|
|
private bool $nullable = false;
|
||
|
|
private $default = null;
|
||
|
|
private bool $autoIncrement = false;
|
||
|
|
private bool $primary = false;
|
||
|
|
private bool $unique = false;
|
||
|
|
|
||
|
|
public function __construct(string $name, string $type)
|
||
|
|
{
|
||
|
|
$this->name = $name;
|
||
|
|
$this->type = $type;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set nullable
|
||
|
|
*/
|
||
|
|
public function nullable(): self
|
||
|
|
{
|
||
|
|
$this->nullable = true;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set default value
|
||
|
|
*/
|
||
|
|
public function default($value): self
|
||
|
|
{
|
||
|
|
$this->default = $value;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set auto increment
|
||
|
|
*/
|
||
|
|
public function autoIncrement(): self
|
||
|
|
{
|
||
|
|
$this->autoIncrement = true;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set primary key
|
||
|
|
*/
|
||
|
|
public function primary(): self
|
||
|
|
{
|
||
|
|
$this->primary = true;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set unique
|
||
|
|
*/
|
||
|
|
public function unique(): self
|
||
|
|
{
|
||
|
|
$this->unique = true;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate SQL
|
||
|
|
*/
|
||
|
|
public function toSql(): string
|
||
|
|
{
|
||
|
|
$sql = "`{$this->name}` {$this->type}";
|
||
|
|
|
||
|
|
if ($this->autoIncrement) {
|
||
|
|
$sql .= " AUTO_INCREMENT";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!$this->nullable) {
|
||
|
|
$sql .= " NOT NULL";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->default !== null) {
|
||
|
|
$default = is_string($this->default) ? "'{$this->default}'" : $this->default;
|
||
|
|
$sql .= " DEFAULT {$default}";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->unique) {
|
||
|
|
$sql .= " UNIQUE";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->primary) {
|
||
|
|
$sql .= " PRIMARY KEY";
|
||
|
|
}
|
||
|
|
|
||
|
|
return $sql;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Foreign key definition
|
||
|
|
*/
|
||
|
|
class ForeignKey
|
||
|
|
{
|
||
|
|
private string $column;
|
||
|
|
private string $references;
|
||
|
|
private string $on;
|
||
|
|
private string $onDelete = 'RESTRICT';
|
||
|
|
private string $onUpdate = 'RESTRICT';
|
||
|
|
|
||
|
|
public function __construct(string $column)
|
||
|
|
{
|
||
|
|
$this->column = $column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set references
|
||
|
|
*/
|
||
|
|
public function references(string $column): self
|
||
|
|
{
|
||
|
|
$this->references = $column;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set on table
|
||
|
|
*/
|
||
|
|
public function on(string $table): self
|
||
|
|
{
|
||
|
|
$this->on = $table;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set on delete action
|
||
|
|
*/
|
||
|
|
public function onDelete(string $action): self
|
||
|
|
{
|
||
|
|
$this->onDelete = $action;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set on update action
|
||
|
|
*/
|
||
|
|
public function onUpdate(string $action): self
|
||
|
|
{
|
||
|
|
$this->onUpdate = $action;
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate SQL
|
||
|
|
*/
|
||
|
|
public function toSql(): string
|
||
|
|
{
|
||
|
|
$sql = "`{$this->column}` INT";
|
||
|
|
|
||
|
|
if ($this->references && $this->on) {
|
||
|
|
$sql .= ", FOREIGN KEY (`{$this->column}`) REFERENCES `{$this->on}` (`{$this->references}`)";
|
||
|
|
$sql .= " ON DELETE {$this->onDelete} ON UPDATE {$this->onUpdate}";
|
||
|
|
}
|
||
|
|
|
||
|
|
return $sql;
|
||
|
|
}
|
||
|
|
}
|