add Conditionable trait for easy WHERE statements

This commit is contained in:
Jan-Niclas Loosen 2025-02-05 16:57:45 +01:00
parent 39bd110344
commit a727216f41
7 changed files with 130 additions and 45 deletions

73
Conditionable.php Normal file
View File

@ -0,0 +1,73 @@
<?php
namespace DatabaseHelper;
use DatabaseHelper\enums\ConditionOperator;
use InvalidArgumentException;
trait Conditionable
{
protected array $conditions = [];
// The Schema instance must be available to validate columns.
protected Schema $table;
/**
* Adds a WHERE condition using an "AND" prefix.
* @param string $col Column name.
* @param string|ConditionOperator $operator Comparison operator as string or enum.
* @param mixed $val Value to compare.
* @return self Current instance for chaining.
* @throws InvalidArgumentException
*/
public function where(string $col, string|ConditionOperator $operator, mixed $val): self {
return $this->addCondition($col, $operator, $val, "AND");
}
/**
* Adds a WHERE condition with an "AND" prefix.
* @param string $col Column name.
* @param string|ConditionOperator $operator Comparison operator as string or enum.
* @param mixed $val Value to compare.
* @return self Current instance for chaining.
* @throws InvalidArgumentException
*/
public function andWhere(string $col, string|ConditionOperator $operator, mixed $val): self {
return $this->addCondition($col, $operator, $val, "AND");
}
/**
* Adds a WHERE condition with an "OR" prefix.
* @param string $col Column name.
* @param string|ConditionOperator $operator Comparison operator as string or enum.
* @param mixed $val Value to compare.
* @return self Current instance for chaining.
* @throws InvalidArgumentException
*/
public function orWhere(string $col, string|ConditionOperator $operator, mixed $val): self {
return $this->addCondition($col, $operator, $val, "OR");
}
protected function addCondition(string $col, string|ConditionOperator $operator, mixed $val, string $prefix): self {
if (!$this->table->existsColumn($col))
throw new InvalidArgumentException("Unknown column: $col");
// Convert the operator string to a ConditionOperator enum if needed.
if (is_string($operator))
$operator = ConditionOperator::fromString($operator);
$columnType = $this->table->columnType($col);
$castedValue = $columnType->dbCast($val);
if (!empty($this->conditions))
$this->conditions[] = $prefix;
$this->conditions[] = "$col " . $operator->toString() . " $castedValue";
return $this;
}
protected function combineConditions(): string {
if (!empty($this->conditions))
return implode(" ", $this->conditions);
return "";
}
}

View File

@ -6,35 +6,35 @@ class Database
/** /**
* Creates a new TableBlueprint instance for the specified table name. * Creates a new TableBlueprint instance for the specified table name.
* @param string $tableName The name of the table to create. * @param string $tableName The name of the table to create.
* @return Table A blueprint instance representing the table. * @return Table instance supporting method chaining
*/ */
public static function makeTable(string $tableName): Table { public static function makeTable(string $tableName): Table {
return new Table($tableName); return new Table($tableName);
} }
public static function makeMigration(Table $table): Migration { public static function makeMigration(Schema $table): Migration {
// TODO: Implement makeMigration() method. // TODO: Implement makeMigration() method.
} }
/** /**
* Creates a new QueryBuilder instance for the specified table. * Creates a new QueryBuilder instance for the specified table.
* @param Table $table The table to query on. * @param Schema $table The table to query on.
* @return Query instance supporting method chaining. * @return Query instance supporting method chaining.
*/ */
public static function makeQuery(Table $table): Query { public static function makeQuery(Schema $table): Query {
return new Query($table); return new Query($table);
} }
public static function makeDeletion(Table $table): Deletion { public static function makeDeletion(Schema $table): Deletion {
// TODO: Implement makeDeletion() method. // TODO: Implement makeDeletion() method.
} }
public static function makeInsertion(Table $table): Insertion { public static function makeInsertion(Schema $table): Insertion {
// TODO: Implement makeInsertion() method. return new Insertion($table);
} }
public static function makeUpdate(Table $table): Update { public static function makeUpdate(Schema $table): Update {
// TODO: Implement makeUpdate() method. // TODO: Implement makeUpdate() method.
} }

View File

@ -6,9 +6,10 @@ use InvalidArgumentException;
class Query class Query
{ {
private Schema $table; use Conditionable;
private array $columns = ['*'];
private array $conditions = []; protected Schema $table;
protected array $columns = ['*'];
public function __construct(Schema $table) { public function __construct(Schema $table) {
$this->table = $table; $this->table = $table;
@ -26,37 +27,11 @@ class Query
return $this; return $this;
} }
public function where(string $col, string $operator, mixed $val): Query {
return $this->addCondition($col, $operator, $val, "AND");
}
public function andWhere(string $col, string $operator, mixed $val): Query {
return $this->addCondition($col, $operator, $val, "AND");
}
public function orWhere(string $col, string $operator, mixed $val): Query {
return $this->addCondition($col, $operator, $val, "OR");
}
private function addCondition(string $col, string $operator, mixed $val, string $prefix): Query {
if ($this->table->existsColumn($col))
throw new InvalidArgumentException("Unknown column: $col");
$columnType = $this->table->columnType($col);
$castedValue = $columnType->dbCast($val);
if (!empty($this->conditions))
$this->conditions[] = $prefix;
$this->conditions[] = "$col $operator $castedValue";
return $this;
}
public function toSql(): string { public function toSql(): string {
$columns = implode(", ", $this->columns); $columns = implode(", ", $this->columns);
$table = $this->table->name; $table = $this->table->name;
$whereClause = !empty($this->conditions) ? " WHERE " . implode(" ", $this->conditions) : ""; $whereClause = $this->combineConditions();
return esc_sql("SELECT $columns FROM $table$whereClause"); return esc_sql("SELECT $columns FROM $table WHERE $whereClause");
} }
public function query(): array { public function query(): array {
@ -66,7 +41,7 @@ class Query
return $this->castResults($results); return $this->castResults($results);
} }
private function castResults(array $results): array { protected function castResults(array $results): array {
foreach ($results as &$row) foreach ($results as &$row)
foreach ($row as $column => &$value) foreach ($row as $column => &$value)
if (isset($this->columnTypes[$column])) if (isset($this->columnTypes[$column]))

View File

@ -3,7 +3,7 @@ namespace DatabaseHelper;
use DatabaseHelper\enums\CharsetTypes; use DatabaseHelper\enums\CharsetTypes;
use DatabaseHelper\enums\CollationTypes; use DatabaseHelper\enums\CollationTypes;
use DatabaseHelper\enums\EngineTypes; use DatabaseHelper\enums\DatabaseEngines;
class Schema class Schema
{ {
@ -13,7 +13,7 @@ class Schema
public array $primaryKey = []; public array $primaryKey = [];
public array $foreignKeys = []; public array $foreignKeys = [];
public EngineTypes $engine = EngineTypes::INNODB; public DatabaseEngines $engine = DatabaseEngines::INNODB;
public CharsetTypes $charset = CharsetTypes::UTF8; public CharsetTypes $charset = CharsetTypes::UTF8;
public CollationTypes $collation = CollationTypes::UTF8_GENERAL_CI; public CollationTypes $collation = CollationTypes::UTF8_GENERAL_CI;

View File

@ -5,7 +5,7 @@ use DatabaseHelper\enums\CascadeTypes;
use DatabaseHelper\enums\CharsetTypes; use DatabaseHelper\enums\CharsetTypes;
use DatabaseHelper\enums\CollationTypes; use DatabaseHelper\enums\CollationTypes;
use DatabaseHelper\enums\ColumnTypes; use DatabaseHelper\enums\ColumnTypes;
use DatabaseHelper\enums\EngineTypes; use DatabaseHelper\enums\DatabaseEngines;
use InvalidArgumentException; use InvalidArgumentException;
class Table class Table
@ -60,7 +60,7 @@ class Table
return $this; return $this;
} }
public function engine(EngineTypes $engine): Table { public function engine(DatabaseEngines $engine): Table {
$this->table->engine = $engine; $this->table->engine = $engine;
return $this; return $this;
} }

View File

@ -0,0 +1,37 @@
<?php
namespace DatabaseHelper\enums;
use InvalidArgumentException;
enum ConditionOperator
{
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 => '<=',
};
}
}

View File

@ -1,7 +1,7 @@
<?php <?php
namespace DatabaseHelper\enums; namespace DatabaseHelper\enums;
enum EngineTypes enum DatabaseEngines
{ {
case INNODB; case INNODB;
case MYISAM; case MYISAM;