Prototype for Schema

This commit is contained in:
Jan-Niclas Loosen 2025-02-05 16:10:30 +01:00
parent 817513095e
commit 39bd110344
4 changed files with 65 additions and 55 deletions

View File

@ -5,11 +5,11 @@ use InvalidArgumentException;
class Insertion class Insertion
{ {
private Table $table; private Schema $table;
private array $currentRow = []; private array $currentRow = [];
private array $batchRows = []; private array $batchRows = [];
public function __construct(Table $table) { public function __construct(Schema $table) {
$this->table = $table; $this->table = $table;
} }
@ -73,6 +73,6 @@ class Insertion
foreach ($this->batchRows as $row) foreach ($this->batchRows as $row)
if (!empty($row)) if (!empty($row))
$wpdb->insert($this->table->tableName, $row); $wpdb->insert($this->table->name, $row);
} }
} }

View File

@ -6,23 +6,23 @@ use InvalidArgumentException;
class Query class Query
{ {
private Table $table; private Schema $table;
private array $columns = ['*']; private array $columns = ['*'];
private array $conditions = []; private array $conditions = [];
/** public function __construct(Schema $table) {
* @var ColumnTypes[]
*/
private array $columnTypes = [];
public function __construct(Table $table) {
$this->columnTypes = array_map(fn($col) => $col['colType'], $table->columns);
$this->table = $table; $this->table = $table;
} }
public function select(string ...$cols): Query { public function select(string ...$cols): Query {
if (!empty($cols)) if (!empty($cols))
$this->columns = $cols; $this->columns = $cols;
// Validate colum existence
foreach ($cols as $col)
if (!$this->table->existsColumn($col))
throw new InvalidArgumentException("Unknown column: $col");
return $this; return $this;
} }
@ -38,23 +38,23 @@ class Query
return $this->addCondition($col, $operator, $val, "OR"); return $this->addCondition($col, $operator, $val, "OR");
} }
private function addCondition(string $colName, string $operator, mixed $val, string $prefix): Query { private function addCondition(string $col, string $operator, mixed $val, string $prefix): Query {
if (!isset($this->columnTypes[$colName])) if ($this->table->existsColumn($col))
throw new InvalidArgumentException("Unknown column: $colName"); throw new InvalidArgumentException("Unknown column: $col");
$columnType = $this->columnTypes[$colName]; $columnType = $this->table->columnType($col);
$castedValue = $columnType->dbCast($val); $castedValue = $columnType->dbCast($val);
if (!empty($this->conditions)) if (!empty($this->conditions))
$this->conditions[] = $prefix; $this->conditions[] = $prefix;
$this->conditions[] = "$colName $operator $castedValue"; $this->conditions[] = "$col $operator $castedValue";
return $this; return $this;
} }
public function toSql(): string { public function toSql(): string {
$columns = implode(", ", $this->columns); $columns = implode(", ", $this->columns);
$table = $this->table->tableName; $table = $this->table->name;
$whereClause = !empty($this->conditions) ? " WHERE " . implode(" ", $this->conditions) : ""; $whereClause = !empty($this->conditions) ? " WHERE " . implode(" ", $this->conditions) : "";
return esc_sql("SELECT $columns FROM $table$whereClause"); return esc_sql("SELECT $columns FROM $table$whereClause");
} }

View File

@ -17,7 +17,19 @@ class Schema
public CharsetTypes $charset = CharsetTypes::UTF8; public CharsetTypes $charset = CharsetTypes::UTF8;
public CollationTypes $collation = CollationTypes::UTF8_GENERAL_CI; public CollationTypes $collation = CollationTypes::UTF8_GENERAL_CI;
public function __construct(string $tableName) { public function __construct(string $name) {
$this->name = $tableName; $this->name = $name;
}
public function existsColumn(string $col): bool {
return isset($this->columns[$col]);
}
public function columnType(string $col) {
return $this->columns[$col]['type'];
}
public function primaryKey() {
return $this->columns['primary']['name'];
} }
} }

View File

@ -17,47 +17,46 @@ class Table
$this->table = new Schema($tableName); $this->table = new Schema($tableName);
} }
public function column(string $colName, ColumnTypes $colType, mixed $default = null, bool $nullable = false, bool $unique = false): Table { public function column(string $name, ColumnTypes $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Table {
if(isset($this->columns[$colName])) if(isset($this->columns[$name]))
throw new InvalidArgumentException('Column name already exists.'); throw new InvalidArgumentException('Column name already exists.');
$this->table->columns[$colName] = [ $this->table->columns[$name] = [
'colName' => $colName, 'name' => $name,
'defaultVal' => $default, 'default' => $default,
'colType' => $colType, 'type' => $type,
'nullable' => $nullable, 'isNullable' => $isNullable,
'unique' => $unique 'isUnique' => $isUnique
]; ];
return $this; return $this;
} }
public function primary(string $colName, ColumnTypes $colType, bool $autoInc = false): Table { public function primary(string $name, ColumnTypes $type, bool $autoInc = false): Table {
if(isset($this->primaryKey)) if(isset($this->primaryKey))
throw new InvalidArgumentException('Primary column already exists.'); throw new InvalidArgumentException('Primary column already exists.');
$this->table->primaryKey = [ $this->table->primaryKey = [
'colName' => $colName, 'name' => $name,
'colType' => $colType, 'autoInc' => $autoInc
'autoincrement' => $autoInc
]; ];
return $this->column($colName, $colType); return $this->column($name, $type);
} }
public function reference(Schema $foreignTable, CascadeTypes $onDelete = CascadeTypes::CASCADE, CascadeTypes $onUpdate = CascadeTypes::CASCADE): Table { public function reference(Schema $foreignTable, CascadeTypes $onDelete = CascadeTypes::CASCADE, CascadeTypes $onUpdate = CascadeTypes::CASCADE): Table {
$colName = $foreignTable->primaryKey['colName']; $name = $foreignTable->primaryKey();
if(isset($this->columns[$colName])) if(isset($this->columns[$name]))
throw new InvalidArgumentException('Column name already exists.'); throw new InvalidArgumentException('Column name already exists.');
$this->table->foreignKeys[] = [ $this->table->foreignKeys[] = [
'colName' => $colName, 'name' => $name,
'tableName' => $foreignTable->name, 'table' => $foreignTable->name,
'onDelete' => $onDelete, 'onDelete' => $onDelete,
'onUpdate' => $onUpdate 'onUpdate' => $onUpdate
]; ];
$this->table->columns[$colName] = $foreignTable->columns[$colName]; $this->table->columns[$name] = $foreignTable->columns[$name];
return $this; return $this;
} }
@ -77,23 +76,21 @@ class Table
} }
public function toSql(): string { public function toSql(): string {
$primaryKey = $this->table->primaryKey();
$sql = "CREATE TABLE `{$this->table->name}` (\n"; $sql = "CREATE TABLE `{$this->table->name}` (\n";
$sql .= " PRIMARY KEY (`$primaryKey`),\n";
// Add primary key constraint if present foreach ($this->table->columns as $col) {
if (!empty($this->primaryKey)) if ($col['name'] !== $primaryKey) {
$sql .= " PRIMARY KEY (`{$this->primaryKey['colName']}`),\n"; $sql .= " `{$col['name']}` {$col['type']->toString()}";
foreach ($this->table->columns as $column) {
if ($column['colName'] !== $this->table->primaryKey['colName']) {
$sql .= " `{$column['colName']}` {$column['colType']->value}";
// Handle nulls, uniqueness, and defaults // Handle nulls, uniqueness, and defaults
if (!$column['nullable']) if (!$col['isNullable'])
$sql .= " NOT NULL"; $sql .= " NOT NULL";
if ($column['unique']) if ($col['isUnique'])
$sql .= " UNIQUE"; $sql .= " UNIQUE";
if ($column['defaultVal'] !== null) { if ($col['default'] !== null) {
$default = is_string($column['defaultVal']) ? "'{$column['defaultVal']}'" : $column['defaultVal']; $default = is_string($col['default']) ? "'{$col['default']}'" : $col['default'];
$sql .= " DEFAULT $default"; $sql .= " DEFAULT $default";
} }
@ -102,15 +99,16 @@ class Table
} }
// Add secondary constraints if present // Add secondary constraints if present
foreach ($this->table->foreignKeys as $foreignKey) { foreach ($this->table->foreignKeys as $key) {
$sql .= " FOREIGN KEY (`{$foreignKey['colName']}`) REFERENCES `{$foreignKey['tableName']}` (`{$foreignKey['colName']}`)"; $sql .= " FOREIGN KEY (`{$key['name']}`) REFERENCES `{$key['table']}` (`{$key['name']}`)";
$sql .= " ON DELETE {$foreignKey['onDelete']->toString()} ON UPDATE {$foreignKey['onUpdate']->toString()},\n"; $sql .= " ON DELETE {$key['onDelete']->toString()} ON UPDATE {$key['onUpdate']->toString()},\n";
} }
// Close the SQL string and add constraints // Close the SQL string and add constraints
$sql = rtrim($sql, ",\n") . "\n"; $sql = rtrim($sql, ",\n") . "\n) ";
$sql .= ") ENGINE={$this->table->engine->toString()} CHARSET={$this->table->charset->toString()} COLLATE={$this->table->collation->toString()};"; $sql .= "ENGINE={$this->table->engine->toString()} ";
$sql .= "CHARSET={$this->table->charset->toString()} ";
$sql -= "COLLATE={$this->table->collation->toString()};";
return esc_sql($sql); return esc_sql($sql);
} }