move from old repository
This commit is contained in:
parent
b684096b4a
commit
29703314a3
49
Conditionable.php
Normal file
49
Conditionable.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Operator;
|
||||
|
||||
trait Conditionable
|
||||
{
|
||||
protected array $conditions = [];
|
||||
protected Schema $schema;
|
||||
|
||||
public function where(string $col, string|Operator $operator, mixed $val): self {
|
||||
return $this->addCondition($col, $operator, $val, "AND");
|
||||
}
|
||||
|
||||
public function andWhere(string $col, string|Operator $operator, mixed $val): self {
|
||||
return $this->addCondition($col, $operator, $val, "AND");
|
||||
}
|
||||
|
||||
public function orWhere(string $col, string|Operator $operator, mixed $val): self {
|
||||
return $this->addCondition($col, $operator, $val, "OR");
|
||||
}
|
||||
|
||||
protected function addCondition(string $col, string|Operator $operator, mixed $val, string $prefix): self {
|
||||
$this->schema->requireColumn($col);
|
||||
|
||||
// Convert the operator string to a ConditionOperator enum if needed.
|
||||
if (is_string($operator))
|
||||
$operator = Operator::fromString($operator);
|
||||
|
||||
$columnType = $this->schema->columnType($col);
|
||||
$castedValue = $columnType->dbCast($val);
|
||||
|
||||
if (!empty($this->conditions))
|
||||
$this->conditions[] = $prefix;
|
||||
$this->conditions[] = "$col " . $operator->toString() . " $castedValue";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isConditioned(): bool {
|
||||
return !empty($this->conditions);
|
||||
}
|
||||
|
||||
protected function combineConditions(): string {
|
||||
if ($this->isConditioned())
|
||||
return implode(" ", $this->conditions);
|
||||
return "";
|
||||
}
|
||||
}
|
54
Database.php
Normal file
54
Database.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
class Database
|
||||
{
|
||||
/**
|
||||
* Creates a new TableBlueprint instance for the specified table name.
|
||||
* @param string $tableName The name of the table to create.
|
||||
* @return Table instance supporting method chaining
|
||||
*/
|
||||
public static function makeTable(string $tableName): Table {
|
||||
return new Table($tableName);
|
||||
}
|
||||
|
||||
public static function makeMigration(Schema $table): Migration {
|
||||
// TODO: Implement makeMigration() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new QueryBuilder instance for the specified table.
|
||||
* @param Schema $table The table to query on.
|
||||
* @return Query instance supporting method chaining.
|
||||
*/
|
||||
public static function makeQuery(Schema $table): Query {
|
||||
return new Query($table);
|
||||
}
|
||||
|
||||
|
||||
public static function makeDeletion(Schema $table): Deletion {
|
||||
// TODO: Implement makeDeletion() method.
|
||||
}
|
||||
|
||||
public static function makeInsertion(Schema $table): Insertion {
|
||||
return new Insertion($table);
|
||||
}
|
||||
|
||||
public static function makeUpdate(Schema $table): Update {
|
||||
return new Update($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the WordPress database prefix to table names if not already present.
|
||||
* @param string ...$tableName Array of table names to process
|
||||
* @return void
|
||||
*/
|
||||
public static function standardizeTableNames(string &...$tableName): void {
|
||||
global $wpdb;
|
||||
$dbPrefix = $wpdb->prefix;
|
||||
|
||||
foreach ($tableName as &$name)
|
||||
if (!str_starts_with($name, $dbPrefix))
|
||||
$name = $dbPrefix . $name;
|
||||
}
|
||||
}
|
62
Deletion.php
Normal file
62
Deletion.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Deletion
|
||||
{
|
||||
use Conditionable;
|
||||
|
||||
protected Schema $table;
|
||||
|
||||
public function __construct(Schema $table) {
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL statement.
|
||||
* @return string SQL query.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function toSql(): string {
|
||||
$table = $this->table->name;
|
||||
$whereClause = $this->combineConditions();
|
||||
|
||||
if (!$this->isConditioned())
|
||||
throw new InvalidArgumentException("Deletions need to be conditioned.");
|
||||
|
||||
return esc_sql("DELETE FROM $table WHERE $whereClause");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the DELETE query.
|
||||
* @return int Number of affected rows.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete(): int {
|
||||
global $wpdb;
|
||||
$query = $this->toSql();
|
||||
$result = $wpdb->query($query);
|
||||
|
||||
if ($result === false)
|
||||
throw new Exception("Deletion failed: " . $wpdb->last_error . ".");
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all entries from the table.
|
||||
* @return int Number of affected rows.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function truncate(): int {
|
||||
global $wpdb;
|
||||
$table = $this->table->name;
|
||||
$query = esc_sql("TRUNCATE TABLE $table");
|
||||
$result = $wpdb->query($query);
|
||||
|
||||
if ($result === false)
|
||||
throw new Exception("Truncation failed: " . $wpdb->last_error . ".");
|
||||
return intval($result);
|
||||
}
|
||||
}
|
84
Insertion.php
Normal file
84
Insertion.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Insertion
|
||||
{
|
||||
private Schema $table;
|
||||
private array $currentRow = [];
|
||||
private array $batchRows = [];
|
||||
|
||||
public function __construct(Schema $table) {
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single column-value pair to the row.
|
||||
* @param string $col Column name.
|
||||
* @param mixed $val Value to insert.
|
||||
* @return Insertion Current instance for method chaining.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function data(string $col, mixed $val): Insertion {
|
||||
if (!isset($this->table->columns[$col]))
|
||||
throw new InvalidArgumentException("Column '$col' does not exist.");
|
||||
|
||||
$columnType = $this->table->columns[$col]['colType'];
|
||||
$this->currentRow[$col] = $columnType->dbCast($val);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple column-value pairs to the row.
|
||||
* @param array $data Associative array of column-value pairs.
|
||||
* @return Insertion Current instance for method chaining.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function batchData(array $data): Insertion {
|
||||
foreach ($data as $key => $value)
|
||||
$this->data($key, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the current row and stacks it for insertion.
|
||||
* @return Insertion Current instance for method chaining.
|
||||
*/
|
||||
public function stack(): Insertion {
|
||||
if (!empty($this->currentRow))
|
||||
$this->stackForInsertion();
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function stackForInsertion(): void {
|
||||
$this->batchRows[] = $this->currentRow;
|
||||
$this->currentRow = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the insertion of all batched rows into the database.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function insert(): bool {
|
||||
global $wpdb;
|
||||
|
||||
// Convert single to batch queries.
|
||||
if (!empty($this->currentRow))
|
||||
$this->stackForInsertion();
|
||||
|
||||
if (empty($this->batchRows))
|
||||
throw new InvalidArgumentException("No data set for insertion.");
|
||||
|
||||
foreach ($this->batchRows as $row) {
|
||||
if (!empty($row)) {
|
||||
$result = $wpdb->insert($this->table->name, $row);
|
||||
if ($result === false)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
139
Migration.php
Normal file
139
Migration.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Propagation;
|
||||
use DatabaseHelper\enums\Type;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Migration
|
||||
{
|
||||
protected Schema $table;
|
||||
protected array $columnsToAdd = [];
|
||||
protected array $columnsToModify = [];
|
||||
protected array $columnsToDrop = [];
|
||||
protected array $primaryKey = null;
|
||||
protected array $foreignKeysToAdd = [];
|
||||
protected array $foreignKeysToDrop = [];
|
||||
|
||||
public function __construct(Schema $table) {
|
||||
$this->table = $table->copy();
|
||||
}
|
||||
|
||||
public function addColumn(string $name, Type $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Migration {
|
||||
$this->table->columns[$name] = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'default' => $default,
|
||||
'isNullable' => $isNullable,
|
||||
'isUnique' => $isUnique
|
||||
];
|
||||
$this->columnsToAdd[] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function modifyColumn(string $name, Type $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Migration {
|
||||
if (!isset($this->table->columns[$name])) {
|
||||
throw new InvalidArgumentException("Column $name does not exist.");
|
||||
}
|
||||
$this->table->columns[$name] = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'default' => $default,
|
||||
'isNullable' => $isNullable,
|
||||
'isUnique' => $isUnique
|
||||
];
|
||||
$this->columnsToModify[] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dropColumn(string $name): Migration {
|
||||
if (!isset($this->table->columns[$name])) {
|
||||
throw new InvalidArgumentException("Column $name does not exist.");
|
||||
}
|
||||
unset($this->table->columns[$name]);
|
||||
$this->columnsToDrop[] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function changePrimaryKey(string $name, bool $autoInc = false): Migration {
|
||||
if (!isset($this->table->columns[$name])) {
|
||||
throw new InvalidArgumentException("Column $name does not exist.");
|
||||
}
|
||||
$this->table->primaryKey = [
|
||||
'name' => $name,
|
||||
'autoInc' => $autoInc
|
||||
];
|
||||
$this->primaryKey = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addForeignKey(string $column, string $referencedTable, string $referencedColumn, Propagation $onDelete, Propagation $onUpdate): Migration {
|
||||
if (!isset($this->table->columns[$column])) {
|
||||
throw new InvalidArgumentException("Column $column does not exist.");
|
||||
}
|
||||
$this->table->foreignKeys[$column] = [
|
||||
'referencedTable' => $referencedTable,
|
||||
'referencedColumn' => $referencedColumn,
|
||||
'onDelete' => $onDelete,
|
||||
'onUpdate' => $onUpdate
|
||||
];
|
||||
$this->foreignKeysToAdd[] = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dropForeignKey(string $name): Migration {
|
||||
if (!isset($this->table->foreignKeys[$name])) {
|
||||
throw new InvalidArgumentException("Foreign key $name does not exist.");
|
||||
}
|
||||
unset($this->table->foreignKeys[$name]);
|
||||
$this->foreignKeysToDrop[] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toSql(): string {
|
||||
$sql = "ALTER TABLE `{$this->table->name}` ";
|
||||
$statements = [];
|
||||
|
||||
foreach ($this->columnsToAdd as $name) {
|
||||
$col = $this->table->columns[$name];
|
||||
$statements[] = "ADD COLUMN `{$col['name']}` {$col['type']->toString()}" .
|
||||
($col['isNullable'] ? "" : " NOT NULL") .
|
||||
($col['isUnique'] ? " UNIQUE" : "") .
|
||||
($col['default'] !== null ? " DEFAULT " . (is_string($col['default']) ? "'{$col['default']}'" : $col['default']) : "");
|
||||
}
|
||||
|
||||
foreach ($this->columnsToModify as $name) {
|
||||
$col = $this->table->columns[$name];
|
||||
$statements[] = "MODIFY COLUMN `{$col['name']}` {$col['type']->toString()}" .
|
||||
($col['isNullable'] ? "" : " NOT NULL") .
|
||||
($col['isUnique'] ? " UNIQUE" : "") .
|
||||
($col['default'] !== null ? " DEFAULT " . (is_string($col['default']) ? "'{$col['default']}'" : $col['default']) : "");
|
||||
}
|
||||
|
||||
foreach ($this->columnsToDrop as $name) {
|
||||
$statements[] = "DROP COLUMN `$name`";
|
||||
}
|
||||
|
||||
foreach ($this->foreignKeysToDrop as $name) {
|
||||
$statements[] = "DROP FOREIGN KEY `$name`";
|
||||
}
|
||||
|
||||
foreach ($this->foreignKeysToAdd as $column) {
|
||||
$fk = $this->table->foreignKeys[$column];
|
||||
$statements[] = "ADD CONSTRAINT `fk_{$column}` FOREIGN KEY (`$column`) REFERENCES `{$fk['referencedTable']}` (`{$fk['referencedColumn']}`) ON DELETE {$fk['onDelete']->toString()} ON UPDATE {$fk['onUpdate']->toString()}";
|
||||
}
|
||||
|
||||
return $sql . implode(", ", $statements) . ";";
|
||||
}
|
||||
|
||||
public function drop(): null {
|
||||
|
||||
}
|
||||
|
||||
public function migrate(): Schema {
|
||||
global $wpdb;
|
||||
$sql = $this->toSql();
|
||||
$wpdb->query($sql);
|
||||
return $this->table;
|
||||
}
|
||||
}
|
109
Query.php
Normal file
109
Query.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Join;
|
||||
use DatabaseHelper\enums\Order;
|
||||
use http\Exception\InvalidArgumentException;
|
||||
use SimplePie\Exception;
|
||||
|
||||
class Query
|
||||
{
|
||||
use Conditionable;
|
||||
|
||||
protected Schema $schema;
|
||||
protected array $columns = ['*'];
|
||||
protected array $joins = [];
|
||||
public array $orderBy;
|
||||
|
||||
public function __construct(Schema $table) {
|
||||
$this->schema = $table;
|
||||
}
|
||||
|
||||
public function select(string ...$cols): Query {
|
||||
if (!empty($cols))
|
||||
$this->columns = $cols;
|
||||
foreach ($cols as $col)
|
||||
$this->schema->requireColumn($col);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orderBy(string $col, Order $order): Query {
|
||||
$this->schema->requireColumn($col);
|
||||
$this->orderBy = [
|
||||
'name' => $col,
|
||||
'order' => $order
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function join(Schema $other, Join $join): Query {
|
||||
$foreignKey = null;
|
||||
if($this->schema->existsReference($other))
|
||||
$foreignKey = $this->schema->foreignKeys[$other->name];
|
||||
if ($other->existsReference($this->schema))
|
||||
$foreignKey = $other->foreignKeys[$this->schema->name];
|
||||
|
||||
if (is_null($foreignKey))
|
||||
throw new InvalidArgumentException('Joins can only applied to referencing columns.');
|
||||
|
||||
// TODO: Implement include instead of merge
|
||||
$this->schema->include($other);
|
||||
$this->joins[] = [
|
||||
'table' => $other->name,
|
||||
'type' => $join
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function isOrdered(): bool {
|
||||
return !empty($this->orderBy);
|
||||
}
|
||||
|
||||
protected function isJoined(): bool {
|
||||
return !empty($this->joins);
|
||||
}
|
||||
|
||||
public function toSql(): string {
|
||||
// Build the SELECT clause.
|
||||
$columns = implode(", ", $this->columns);
|
||||
$primaryTable = $this->schema->name;
|
||||
$sqlStatement = "SELECT $columns FROM $primaryTable";
|
||||
|
||||
// Append join clauses, if any.
|
||||
if ($this->isJoined()) {
|
||||
foreach ($this->joins as $join) {
|
||||
$sqlStatement .= " " . $join['type']->toString() . " NATURAL JOIN " . $join['table'];
|
||||
}
|
||||
}
|
||||
|
||||
// Append the WHERE clause if conditions exist.
|
||||
if ($this->isConditioned()) {
|
||||
$whereClause = $this->combineConditions();
|
||||
$sqlStatement .= " WHERE $whereClause";
|
||||
}
|
||||
|
||||
// Append the ORDER BY clause if ordering is set.
|
||||
if ($this->isOrdered()) {
|
||||
$orderClause = $this->orderBy['name'] . ' ' . $this->orderBy['order'];
|
||||
$sqlStatement .= " ORDER BY $orderClause";
|
||||
}
|
||||
|
||||
return esc_sql($sqlStatement);
|
||||
}
|
||||
|
||||
public function query(): array {
|
||||
global $wpdb;
|
||||
$query = $this->toSql();
|
||||
$results = $wpdb->get_results($query, ARRAY_A);
|
||||
return $this->castResults($results);
|
||||
}
|
||||
|
||||
protected function castResults(array $results): array {
|
||||
foreach ($results as &$row)
|
||||
foreach ($row as $column => &$value)
|
||||
if (isset($this->columnTypes[$column]))
|
||||
$value = $this->columnTypes[$column]->valCast($value);
|
||||
return $results;
|
||||
}
|
||||
}
|
78
Schema.php
Normal file
78
Schema.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Charset;
|
||||
use DatabaseHelper\enums\Collation;
|
||||
use DatabaseHelper\enums\Engine;
|
||||
use http\Exception\InvalidArgumentException;
|
||||
|
||||
class Schema
|
||||
{
|
||||
public string $name = '';
|
||||
|
||||
public array $columns = [];
|
||||
public array $primaryKey = [];
|
||||
public array $foreignKeys = [];
|
||||
|
||||
public Engine $engine = Engine::INNODB;
|
||||
public Charset $charset = Charset::UTF8;
|
||||
public Collation $collation = Collation::UTF8_GENERAL_CI;
|
||||
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function requireColumn(string $col): void {
|
||||
if (!$this->existsColumn($col))
|
||||
throw new InvalidArgumentException("Column '$col' is not defined.");
|
||||
}
|
||||
|
||||
public function existsColumn(string $col): bool {
|
||||
return isset($this->columns[$col]);
|
||||
}
|
||||
|
||||
public function existsReference(Schema $schema): bool {
|
||||
foreach ($this->foreignKeys as $foreignKey)
|
||||
if ($foreignKey['table'] == $schema->name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function columnType(string $col) {
|
||||
return $this->columns[$col]['type'];
|
||||
}
|
||||
|
||||
public function primaryKey() {
|
||||
return $this->columns['primary']['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep copy of this instance.
|
||||
* @return Schema
|
||||
*/
|
||||
public function copy(): Schema {
|
||||
$copy = new Schema($this->name);
|
||||
$copy->columns = genericDeepCopy($this->columns);
|
||||
$copy->primaryKey = genericDeepCopy($this->primaryKey);
|
||||
$copy->foreignKeys = genericDeepCopy($this->foreignKeys);
|
||||
$copy->engine = $this->engine;
|
||||
$copy->charset = $this->charset;
|
||||
$copy->collation = $this->collation;
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function include(Schema $other): void {
|
||||
// Create a copy of the other schema.
|
||||
$otherCopy = $other->copy();
|
||||
|
||||
// Add any column that isn't already defined.
|
||||
foreach ($otherCopy->columns as $colName => $colDef)
|
||||
if (!isset($this->columns[$colName]))
|
||||
$this->columns[$colName] = $colDef;
|
||||
|
||||
// Add any foreign key that doesn't already exist.
|
||||
foreach ($other->foreignKeys as $colName => $foreignKey)
|
||||
if (!isset($this->foreignKeys[$colName]))
|
||||
$this->foreignKeys[$colName] = $foreignKey;
|
||||
}
|
||||
}
|
131
Table.php
Normal file
131
Table.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Propagation;
|
||||
use DatabaseHelper\enums\Charset;
|
||||
use DatabaseHelper\enums\Collation;
|
||||
use DatabaseHelper\enums\Type;
|
||||
use DatabaseHelper\enums\Engine;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Table
|
||||
{
|
||||
protected Schema $table;
|
||||
|
||||
public function __construct(string $tableName) {
|
||||
Database::standardizeTableNames($tableName);
|
||||
$this->table = new Schema($tableName);
|
||||
}
|
||||
|
||||
public function column(string $name, Type $type, mixed $default = null, bool $isNullable = false, bool $isUnique = false): Table {
|
||||
$this->table->columns[$name] = [
|
||||
'name' => $name,
|
||||
'default' => $default,
|
||||
'type' => $type,
|
||||
'isNullable' => $isNullable,
|
||||
'isUnique' => $isUnique
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function primary(string $name, Type $type, bool $autoInc = false): Table {
|
||||
if(isset($this->primaryKey))
|
||||
throw new InvalidArgumentException('Primary column already exists.');
|
||||
|
||||
$this->table->primaryKey = [
|
||||
'name' => $name,
|
||||
'autoInc' => $autoInc
|
||||
];
|
||||
|
||||
return $this->column($name, $type);
|
||||
}
|
||||
|
||||
public function reference(Schema $foreignTable, Propagation $onDelete = Propagation::CASCADE, Propagation $onUpdate = Propagation::CASCADE): Table {
|
||||
$name = $foreignTable->primaryKey();
|
||||
if(isset($this->columns[$name]))
|
||||
throw new InvalidArgumentException('Column name already exists.');
|
||||
|
||||
$this->table->foreignKeys[$name] = [
|
||||
'name' => $name,
|
||||
'table' => $foreignTable->name,
|
||||
'onDelete' => $onDelete,
|
||||
'onUpdate' => $onUpdate
|
||||
];
|
||||
|
||||
$this->table->columns[$name] = $foreignTable->columns[$name];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function engine(Engine $engine): Table {
|
||||
$this->table->engine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function charset(Charset $charset): Table {
|
||||
$this->table->charset = $charset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function collation(Collation $collation): Table {
|
||||
$this->table->collation = $collation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL statement.
|
||||
* @return string SQL query.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function toSql(): string {
|
||||
$primaryKey = $this->table->primaryKey();
|
||||
|
||||
$sql = "CREATE TABLE `{$this->table->name}` (\n";
|
||||
$sql .= " PRIMARY KEY (`$primaryKey`),\n";
|
||||
|
||||
foreach ($this->table->columns as $col) {
|
||||
if ($col['name'] !== $primaryKey) {
|
||||
$sql .= " `{$col['name']}` {$col['type']->toString()}";
|
||||
// Handle nulls, uniqueness, and defaults
|
||||
if (!$col['isNullable'])
|
||||
$sql .= " NOT NULL";
|
||||
if ($col['isUnique'])
|
||||
$sql .= " UNIQUE";
|
||||
if ($col['default'] !== null) {
|
||||
$default = is_string($col['default']) ? "'{$col['default']}'" : $col['default'];
|
||||
$sql .= " DEFAULT $default";
|
||||
}
|
||||
|
||||
$sql .= ",\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add secondary constraints if present
|
||||
foreach ($this->table->foreignKeys as $key) {
|
||||
$sql .= " FOREIGN KEY (`{$key['name']}`) REFERENCES `{$key['table']}` (`{$key['name']}`)";
|
||||
$sql .= " ON DELETE {$key['onDelete']->toString()} ON UPDATE {$key['onUpdate']->toString()},\n";
|
||||
}
|
||||
|
||||
// Close the SQL string and add constraints
|
||||
$sql = rtrim($sql, ",\n") . "\n) ";
|
||||
$sql .= "ENGINE={$this->table->engine->toString()} ";
|
||||
$sql .= "CHARSET={$this->table->charset->toString()} ";
|
||||
$sql -= "COLLATE={$this->table->collation->toString()};";
|
||||
return esc_sql($sql);
|
||||
}
|
||||
|
||||
public function create(): Schema {
|
||||
global $wpdb;
|
||||
|
||||
if (empty($this->primaryKey))
|
||||
throw new InvalidArgumentException('A primary key must be defined.');
|
||||
$table_name = $wpdb->prefix . $this->table->name;
|
||||
|
||||
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
|
||||
$sql = $this->toSql();
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
dbDelta($sql);
|
||||
}
|
||||
return $this->table;
|
||||
}
|
||||
}
|
69
Update.php
Normal file
69
Update.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace DatabaseHelper;
|
||||
|
||||
use DatabaseHelper\enums\Type;
|
||||
use DatabaseHelper\enums\Operator;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Update
|
||||
{
|
||||
use Conditionable;
|
||||
|
||||
protected Schema $table;
|
||||
protected array $values = [];
|
||||
|
||||
public function __construct(Schema $table) {
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value for a column.
|
||||
* @param string $col Column name.
|
||||
* @param mixed $val Value to set.
|
||||
* @return self Current instance for method chaining.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function set(string $col, mixed $val): self {
|
||||
$this->table->requireColumn($col);
|
||||
|
||||
$columnType = $this->table->columnType($col);
|
||||
$castedValue = $columnType->dbCast($val);
|
||||
$this->values[$col] = $castedValue;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL statement.
|
||||
* @return string SQL query.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function toSql(): string {
|
||||
if (empty($this->values))
|
||||
throw new InvalidArgumentException("No values have been set for the Update.");
|
||||
|
||||
$table = $this->table->name;
|
||||
$setParts = [];
|
||||
foreach ($this->values as $col => $value)
|
||||
$setParts[] = "$col = $value";
|
||||
$setClause = implode(", ", $setParts);
|
||||
|
||||
// FINAL SQL STATEMENT
|
||||
$sqlStatement = "UPDATE $table SET $setClause";
|
||||
if($this->isConditioned()) {
|
||||
$whereClause = $this->combineConditions();
|
||||
$sqlStatement .= " WHERE $whereClause";
|
||||
}
|
||||
return esc_sql($sqlStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL UPDATE query.
|
||||
* @return bool Returns true on success.
|
||||
*/
|
||||
public function update(): bool {
|
||||
global $wpdb;
|
||||
$query = $this->toSql();
|
||||
$result = $wpdb->query($query);
|
||||
return boolval($result);
|
||||
}
|
||||
}
|
42
build.php
Normal file
42
build.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// Helpers
|
||||
function genericDeepCopy(mixed $data): mixed {
|
||||
if (is_array($data)) {
|
||||
$copy = [];
|
||||
foreach ($data as $key => $value)
|
||||
$copy[$key] = genericDeepCopy($value);
|
||||
return $copy;
|
||||
}
|
||||
elseif (is_object($data)) {
|
||||
// If the object supports a copy method.
|
||||
if (method_exists($data, 'copy'))
|
||||
return $data->copy();
|
||||
// Use PHP's clone as fallback.
|
||||
return clone $data;
|
||||
}
|
||||
else {
|
||||
// Scalar variables.
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
// Require all enums
|
||||
require './enums/Propagation.php';
|
||||
require './enums/Operator.php';
|
||||
require './enums/Order.php';
|
||||
require './enums/Charset.php';
|
||||
require './enums/Collation.php';
|
||||
require './enums/Type.php';
|
||||
require './enums/Engine.php';
|
||||
require './enums/Join.php';
|
||||
|
||||
// Require all classes
|
||||
require './Database.php';
|
||||
require './Conditionable.php';
|
||||
require './Deletion.php';
|
||||
require './Insertion.php';
|
||||
require './Migration.php';
|
||||
require './Query.php';
|
||||
require './Schema.php';
|
||||
require './Table.php';
|
||||
require './Update.php';
|
40
enums/Charset.php
Normal file
40
enums/Charset.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Charset
|
||||
{
|
||||
case UTF8;
|
||||
case UTF8MB4;
|
||||
case LATIN1;
|
||||
case ASCII;
|
||||
case UTF16;
|
||||
case UTF32;
|
||||
case LATIN2;
|
||||
case LATIN5;
|
||||
case CP1251;
|
||||
case CP850;
|
||||
case TIS620;
|
||||
case GREEK;
|
||||
case HEBREW;
|
||||
case BINARY;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::UTF8 => 'utf8',
|
||||
self::UTF8MB4 => 'utf8mb4',
|
||||
self::LATIN1 => 'latin1',
|
||||
self::ASCII => 'ascii',
|
||||
self::UTF16 => 'utf16',
|
||||
self::UTF32 => 'utf32',
|
||||
self::LATIN2 => 'latin2',
|
||||
self::LATIN5 => 'latin5',
|
||||
self::CP1251 => 'cp1251',
|
||||
self::CP850 => 'cp850',
|
||||
self::TIS620 => 'tis620',
|
||||
self::GREEK => 'greek',
|
||||
self::HEBREW => 'hebrew',
|
||||
self::BINARY => 'binary',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
32
enums/Collation.php
Normal file
32
enums/Collation.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Collation
|
||||
{
|
||||
case UTF8_GENERAL_CI;
|
||||
case UTF8_UNICODE_CI;
|
||||
case UTF8_SPANISH_CI;
|
||||
case UTF8MB4_UNICODE_CI;
|
||||
case UTF8MB4_GENERAL_CI;
|
||||
case UTF8MB4_UNICODE_520_CI;
|
||||
case LATIN1_SWEDISH_CI;
|
||||
case UTF8MB4_BIN;
|
||||
case UTF8_BIN;
|
||||
case LATIN1_BIN;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::UTF8_GENERAL_CI => 'utf8_general_ci',
|
||||
self::UTF8_UNICODE_CI => 'utf8_unicode_ci',
|
||||
self::UTF8_SPANISH_CI => 'utf8_spanish_ci',
|
||||
self::UTF8MB4_UNICODE_CI => 'utf8mb4_unicode_ci',
|
||||
self::UTF8MB4_GENERAL_CI => 'utf8mb4_general_ci',
|
||||
self::UTF8MB4_UNICODE_520_CI => 'utf8mb4_unicode_520_ci',
|
||||
self::LATIN1_SWEDISH_CI => 'latin1_swedish_ci',
|
||||
self::UTF8MB4_BIN => 'utf8mb4_bin',
|
||||
self::UTF8_BIN => 'utf8_bin',
|
||||
self::LATIN1_BIN => 'latin1_bin',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
15
enums/Engine.php
Normal file
15
enums/Engine.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Engine
|
||||
{
|
||||
case INNODB;
|
||||
case MYISAM;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::INNODB => 'InnoDB',
|
||||
self::MYISAM => 'MyISAM',
|
||||
};
|
||||
}
|
||||
}
|
20
enums/Join.php
Normal file
20
enums/Join.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Join
|
||||
{
|
||||
case INNER;
|
||||
case LEFT;
|
||||
case RIGHT;
|
||||
case FULL;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::INNER => 'INNER JOIN',
|
||||
self::LEFT => 'LEFT JOIN',
|
||||
self::RIGHT => 'RIGHT JOIN',
|
||||
self::FULL => 'FULL JOIN',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
37
enums/Operator.php
Normal file
37
enums/Operator.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
enum Operator
|
||||
{
|
||||
case EQUAL;
|
||||
case NOT_EQUAL;
|
||||
case GREATER;
|
||||
case GREATER_EQUAL;
|
||||
case LESS;
|
||||
case LESS_EQUAL;
|
||||
|
||||
public static function fromString(string $operator): self {
|
||||
return match ($operator) {
|
||||
'=' => self::EQUAL,
|
||||
'!=' => self::NOT_EQUAL,
|
||||
'>' => self::GREATER,
|
||||
'>=' => self::GREATER_EQUAL,
|
||||
'<' => self::LESS,
|
||||
'<=' => self::LESS_EQUAL,
|
||||
default => throw new InvalidArgumentException("Invalid operator: $operator"),
|
||||
};
|
||||
}
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::EQUAL => '=',
|
||||
self::NOT_EQUAL => '!=',
|
||||
self::GREATER => '>',
|
||||
self::GREATER_EQUAL => '>=',
|
||||
self::LESS => '<',
|
||||
self::LESS_EQUAL => '<=',
|
||||
};
|
||||
}
|
||||
}
|
16
enums/Order.php
Normal file
16
enums/Order.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Order
|
||||
{
|
||||
case ASC;
|
||||
case DESC;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::ASC => 'ASC',
|
||||
self::DESC => 'DESC',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
21
enums/Propagation.php
Normal file
21
enums/Propagation.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
|
||||
enum Propagation
|
||||
{
|
||||
case CASCADE;
|
||||
case SET_NULL;
|
||||
case NO_ACTION;
|
||||
case RESTRICT;
|
||||
case SET_DEFAULT;
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::CASCADE => 'CASCADE',
|
||||
self::SET_NULL => 'SET NULL',
|
||||
self::NO_ACTION => 'NO ACTION',
|
||||
self::RESTRICT => 'RESTRICT',
|
||||
self::SET_DEFAULT => 'SET DEFAULT',
|
||||
};
|
||||
}
|
||||
}
|
46
enums/Type.php
Normal file
46
enums/Type.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace DatabaseHelper\enums;
|
||||
use DateTime;
|
||||
|
||||
enum Type
|
||||
{
|
||||
case INT;
|
||||
case FLOAT;
|
||||
case STRING;
|
||||
case BOOL;
|
||||
case ARRAY;
|
||||
case DATE;
|
||||
|
||||
public function valCast(mixed $value): mixed {
|
||||
return match ($this) {
|
||||
self::INT => (int) $value,
|
||||
self::FLOAT => (float) $value,
|
||||
self::STRING => (string) $value,
|
||||
self::BOOL => filter_var($value, FILTER_VALIDATE_BOOLEAN),
|
||||
self::ARRAY => is_array($value) ? $value : (json_decode($value, true) ?? []),
|
||||
self::DATE => $value instanceof DateTime ? $value : (strtotime($value) ? new DateTime($value) : null),
|
||||
};
|
||||
}
|
||||
|
||||
public function dbCast(mixed $value): mixed {
|
||||
return match ($this) {
|
||||
self::INT => intval($value),
|
||||
self::FLOAT => floatval($value),
|
||||
self::STRING => esc_sql($value),
|
||||
self::BOOL => filter_var($value, FILTER_VALIDATE_BOOLEAN) ? "TRUE" : "FALSE",
|
||||
self::ARRAY => json_encode($value),
|
||||
self::DATE => $value instanceof DateTime ? $value->format('Y-m-d H:i:s') : json_encode($value)
|
||||
};
|
||||
}
|
||||
|
||||
public function toString(): string {
|
||||
return match ($this) {
|
||||
self::INT => 'INTEGER',
|
||||
self::FLOAT => 'FLOAT',
|
||||
self::STRING => 'VARCHAR(255)',
|
||||
self::BOOL => 'BOOLEAN',
|
||||
self::ARRAY => 'JSON',
|
||||
self::DATE => 'DATETIME',
|
||||
};
|
||||
}
|
||||
}
|
53
index.php
Normal file
53
index.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Database Helper
|
||||
* Version: 1.0
|
||||
* Author: Jan-Niclas Loosen (Loosen-IT and Designraketen GmbH)
|
||||
* Author URI: https://example.com
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
use DatabaseHelper\Database;
|
||||
use DatabaseHelper\enums\Order;
|
||||
use DatabaseHelper\enums\Type;
|
||||
|
||||
try {
|
||||
require 'build.php';
|
||||
|
||||
$table = Database::makeTable('testing-table')
|
||||
->primary('col-primary', Type::INT, autoInc: true)
|
||||
->column('col-one', Type::BOOL)
|
||||
->column('col-two', Type::STRING)
|
||||
->create();
|
||||
|
||||
$batches = [
|
||||
[
|
||||
['col-one', true],
|
||||
['col-two', 'EXPLODING!!!']
|
||||
],
|
||||
[
|
||||
['col-one', false],
|
||||
['col-two', 'EXPLODING!!!']
|
||||
],
|
||||
];
|
||||
|
||||
$batchInsert = Database::makeInsertion($table);
|
||||
foreach($batches as $batch) {
|
||||
$batchInsert->batchData($batch)->stack();
|
||||
}
|
||||
$batchInsert->insert();
|
||||
|
||||
Database::makeUpdate($table)
|
||||
->where('col-primary', '=', 1)
|
||||
->set('col-one', false)
|
||||
->update();
|
||||
|
||||
$results = Database::makeQuery($table)
|
||||
->select('col-primary', 'col-one')
|
||||
->where('col-one', '=', true)
|
||||
->orderBy('col-one', Order::DESC)
|
||||
->query();
|
||||
}
|
||||
catch ( Exception $e ) {
|
||||
echo $e->getMessage();
|
||||
}
|
Loading…
Reference in New Issue
Block a user