parent
0606c3bea8
commit
e71e8330f9
@ -0,0 +1,85 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Database\Grammar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Description of Grammar |
||||||
|
* |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
abstract class Grammar |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Compile an entire SELECT statement. |
||||||
|
*/ |
||||||
|
public function compileSelect($query): string |
||||||
|
{ |
||||||
|
$sql = 'SELECT '; |
||||||
|
$sql .= $query->select; |
||||||
|
|
||||||
|
$sql .= ' FROM '; |
||||||
|
$sql .= $this->wrap($query->table); |
||||||
|
|
||||||
|
$sql .= $this->compileJoins($query); |
||||||
|
|
||||||
|
$sql .= $this->compileWhere($query); |
||||||
|
|
||||||
|
$sql .= $this->compileGroupBy($query); |
||||||
|
|
||||||
|
$sql .= $this->compileHaving($query); |
||||||
|
|
||||||
|
$sql .= $this->compileOrderBy($query); |
||||||
|
|
||||||
|
$sql .= $this->compileLimit($query); |
||||||
|
|
||||||
|
$sql .= $this->compileOffset($query); |
||||||
|
|
||||||
|
return $sql; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public function compileLimit(?int $limit): string |
||||||
|
{ |
||||||
|
return $limit !== null ? " LIMIT {$limit}" : ''; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileOffset(?int $offset): string |
||||||
|
{ |
||||||
|
return $offset !== null ? " OFFSET {$offset}" : ''; |
||||||
|
} |
||||||
|
|
||||||
|
protected function compileOrderBy($query): string |
||||||
|
{ |
||||||
|
if (!$query->orders) |
||||||
|
return ''; |
||||||
|
|
||||||
|
$parts = []; |
||||||
|
|
||||||
|
foreach ($query->orders as $order) { |
||||||
|
|
||||||
|
$parts[] = |
||||||
|
$this->wrap($order['column']) |
||||||
|
.' ' |
||||||
|
.$order['direction']; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return ' ORDER BY '.implode(',', $parts); |
||||||
|
} |
||||||
|
|
||||||
|
abstract public function wrap(string $identifier): string; |
||||||
|
abstract public function compileDate(string $column): string; |
||||||
|
abstract public function compileTime(string $column): string; |
||||||
|
abstract public function compileYear(string $column): string; |
||||||
|
abstract public function compileMonth(string $column): string; |
||||||
|
abstract public function compileDay(string $column): string; |
||||||
|
abstract public function compileJsonContains(string $column): string; |
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Database\Grammar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Description of MySQLGrammar |
||||||
|
* |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
class MySQLGrammar extends Grammar |
||||||
|
{ |
||||||
|
public function wrap(string $column): string |
||||||
|
{ |
||||||
|
return "`{$column}`"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileYear(string $column): string |
||||||
|
{ |
||||||
|
return "YEAR({$this->wrap($column)})"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileMonth(string $column): string |
||||||
|
{ |
||||||
|
return "MONTH({$this->wrap($column)})"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileDay(string $column): string |
||||||
|
{ |
||||||
|
return "DAY({$this->wrap($column)})"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileDate(string $column): string |
||||||
|
{ |
||||||
|
return "DATE({$this->wrap($column)})"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileTime(string $column): string |
||||||
|
{ |
||||||
|
return "TIME({$this->wrap($column)})"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileJsonContains(string $column): string |
||||||
|
{ |
||||||
|
return "JSON_CONTAINS({$this->wrap($column)}, ?)"; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Database\Grammar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Description of SQLiteGrammar |
||||||
|
* |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
class SQLiteGrammar extends Grammar |
||||||
|
{ |
||||||
|
public function wrap(string $column): string |
||||||
|
{ |
||||||
|
return "\"{$column}\""; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileYear(string $column): string |
||||||
|
{ |
||||||
|
return "strftime('%Y',".$this->wrap($column).")"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileMonth(string $column): string |
||||||
|
{ |
||||||
|
return "strftime('%m',".$this->wrap($column).")"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileDay(string $column): string |
||||||
|
{ |
||||||
|
return "strftime('%d',".$this->wrap($column).")"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileDate(string $column): string |
||||||
|
{ |
||||||
|
return "date(".$this->wrap($column).")"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileTime(string $column): string |
||||||
|
{ |
||||||
|
return "time(".$this->wrap($column).")"; |
||||||
|
} |
||||||
|
|
||||||
|
public function compileJsonContains(string $column): string |
||||||
|
{ |
||||||
|
return "EXISTS ( |
||||||
|
SELECT 1 |
||||||
|
FROM json_each(".$this->wrap($column).") |
||||||
|
WHERE value=? |
||||||
|
)"; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,745 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
/* |
||||||
|
* @author Robert Strutts |
||||||
|
* @copyright (c) 2026, Robert Strutts |
||||||
|
* @license MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace IOcornerstone\Framework\Trait\Database; |
||||||
|
|
||||||
|
use IOcornerstone\Framework\Database\Grammar\{ |
||||||
|
MySQLGrammar, |
||||||
|
SQLiteGrammar, |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Robert Strutts |
||||||
|
*/ |
||||||
|
trait QueryBuilder |
||||||
|
{ |
||||||
|
|
||||||
|
protected $grammar; |
||||||
|
protected array $wheres = []; |
||||||
|
protected array $bindings = []; |
||||||
|
protected array $joins = []; |
||||||
|
protected array $groups = []; |
||||||
|
protected array $havings = []; |
||||||
|
protected array $orders = []; |
||||||
|
protected ?string $orderBy = null; |
||||||
|
protected ?int $limit = null; |
||||||
|
protected ?int $offset = null; |
||||||
|
protected string $select = '*'; |
||||||
|
|
||||||
|
public function select(array $columns): static |
||||||
|
{ |
||||||
|
$this->select = implode(',', $columns); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function where( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
mixed $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Basic', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator, |
||||||
|
'value' => $value |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhere( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
mixed $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'Basic', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator, |
||||||
|
'value' => $value |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereNull(string $column): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Null', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereNull(string $column): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'Null', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereNotNull(string $column): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'NotNull', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereNotNull(string $column): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'NotNull', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereIn( |
||||||
|
string $column, |
||||||
|
array $values |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'In', |
||||||
|
'column' => $column, |
||||||
|
'count' => count($values) |
||||||
|
]; |
||||||
|
|
||||||
|
foreach ($values as $value) |
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereIn( |
||||||
|
string $column, |
||||||
|
array $values |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'In', |
||||||
|
'column' => $column, |
||||||
|
'count' => count($values) |
||||||
|
]; |
||||||
|
|
||||||
|
foreach ($values as $value) |
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereNotIn( |
||||||
|
string $column, |
||||||
|
array $values |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'NotIn', |
||||||
|
'column' => $column, |
||||||
|
'count' => count($values) |
||||||
|
]; |
||||||
|
|
||||||
|
foreach ($values as $value) |
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereBetween( |
||||||
|
string $column, |
||||||
|
mixed $from, |
||||||
|
mixed $to |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Between', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $from; |
||||||
|
$this->bindings[] = $to; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereNotBetween( |
||||||
|
string $column, |
||||||
|
mixed $from, |
||||||
|
mixed $to |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'NotBetween', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $from; |
||||||
|
$this->bindings[] = $to; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereLike( |
||||||
|
string $column, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Like', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereNotLike( |
||||||
|
string $column, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'NotLike', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereColumn( |
||||||
|
string $left, |
||||||
|
string $operator, |
||||||
|
string $right |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Column', |
||||||
|
'left' => $left, |
||||||
|
'operator' => $operator, |
||||||
|
'right' => $right |
||||||
|
]; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereRaw( |
||||||
|
string $sql, |
||||||
|
array $bindings = [] |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Raw', |
||||||
|
'sql' => $sql |
||||||
|
]; |
||||||
|
|
||||||
|
foreach ($bindings as $binding) { |
||||||
|
$this->bindings[] = $binding; |
||||||
|
} |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereDate( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Date', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator, |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereTime( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Time', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereYear( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
int $year |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Year', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $year; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereMonth( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
int $month |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Month', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $month; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereDay( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
int $day |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'Day', |
||||||
|
'column' => $column, |
||||||
|
'operator' => $operator |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $day; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereBetween( |
||||||
|
string $column, |
||||||
|
mixed $from, |
||||||
|
mixed $to |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'Between', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $from; |
||||||
|
$this->bindings[] = $to; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereNotBetween( |
||||||
|
string $column, |
||||||
|
mixed $from, |
||||||
|
mixed $to |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'NotBetween', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $from; |
||||||
|
$this->bindings[] = $to; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereLike( |
||||||
|
string $column, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'Like', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orWhereNotLike( |
||||||
|
string $column, |
||||||
|
string $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'OR', |
||||||
|
'type' => 'NotLike', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function whereJsonContains( |
||||||
|
string $column, |
||||||
|
mixed $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->wheres[] = [ |
||||||
|
'boolean' => 'AND', |
||||||
|
'type' => 'JsonContains', |
||||||
|
'column' => $column |
||||||
|
]; |
||||||
|
|
||||||
|
$this->bindings[] = json_encode($value); |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function orderBy( |
||||||
|
string $column, |
||||||
|
string $direction = 'ASC' |
||||||
|
): static |
||||||
|
{ |
||||||
|
$direction = strtoupper($direction); |
||||||
|
|
||||||
|
$this->orderBy = "{$column} {$direction}"; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function limit(int $limit): static |
||||||
|
{ |
||||||
|
$this->limit = $limit; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function leftJoin( |
||||||
|
string $table, |
||||||
|
string $left, |
||||||
|
string $operator, |
||||||
|
string $right |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->joins[] = "LEFT JOIN {$table} |
||||||
|
ON {$left} {$operator} {$right}"; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function join( |
||||||
|
string $table, |
||||||
|
string $left, |
||||||
|
string $operator, |
||||||
|
string $right |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->joins[] = "INNER JOIN {$table} ON {$left} {$operator} {$right}"; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
private function buildWhere(string $sql): string |
||||||
|
{ |
||||||
|
foreach ($this->wheres as $index => $where) { |
||||||
|
|
||||||
|
if ($index > 0) { |
||||||
|
$sql .= ' ' . $where['boolean'] . ' '; |
||||||
|
} |
||||||
|
|
||||||
|
switch ($where['type']) { |
||||||
|
case 'Null': |
||||||
|
$sql .= "{$where['column']} IS NULL"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'NotNull': |
||||||
|
$sql .= "{$where['column']} IS NOT NULL"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Like': |
||||||
|
$sql .= "{$where['column']} LIKE ?"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'NotLike': |
||||||
|
$sql .= "{$where['column']} NOT LIKE ?"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Between': |
||||||
|
$sql .= "{$where['column']} BETWEEN ? AND ?"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'NotBetween': |
||||||
|
$sql .= "{$where['column']} NOT BETWEEN ? AND ?"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'In': |
||||||
|
$sql .= "{$where['column']} IN (" . |
||||||
|
implode(',', array_fill(0, $where['count'], '?')) . ")"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'NotIn': |
||||||
|
$sql .= "{$where['column']} NOT IN (" . |
||||||
|
implode(',', array_fill(0, $where['count'], '?')) . ")"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Column': |
||||||
|
$sql .= "{$where['left']} {$where['operator']} {$where['right']}"; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Raw': |
||||||
|
$sql .= $where['sql']; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Basic': |
||||||
|
|
||||||
|
$sql .= $this->grammar->wrap($where['column']); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'Year': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileYear( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'Month': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileMonth( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'Day': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileDay( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'Date': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileDate( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'Time': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileTime( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
$sql .= " {$where['operator']} ?"; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'JsonContains': |
||||||
|
|
||||||
|
$sql .= $this->grammar->compileJsonContains( |
||||||
|
$where['column'] |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return $sql; |
||||||
|
} |
||||||
|
|
||||||
|
protected function buildSql(): string |
||||||
|
{ |
||||||
|
if ($this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'mysql') { |
||||||
|
$this->grammar = new MySQLGrammar(); |
||||||
|
} else { |
||||||
|
$this->grammar = new SQLiteGrammar(); |
||||||
|
} |
||||||
|
|
||||||
|
$sql = "SELECT {$this->select} FROM {$this->table}"; |
||||||
|
|
||||||
|
if ($this->joins) { |
||||||
|
$sql .= ' ' . implode(' ', $this->joins); |
||||||
|
} |
||||||
|
|
||||||
|
if ($this->wheres) { |
||||||
|
$sql = $this->buildWhere($sql . " WHERE "); |
||||||
|
} |
||||||
|
|
||||||
|
if ($this->orderBy) { |
||||||
|
$sql .= ' ORDER BY ' . $this->orderBy; |
||||||
|
} |
||||||
|
|
||||||
|
if ($this->limit) { |
||||||
|
$sql .= ' LIMIT ' . $this->limit; |
||||||
|
} |
||||||
|
|
||||||
|
return $sql; |
||||||
|
} |
||||||
|
|
||||||
|
public function groupBy(string ...$columns): static |
||||||
|
{ |
||||||
|
foreach ($columns as $column) { |
||||||
|
$this->groups[] = $column; |
||||||
|
} |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function having( |
||||||
|
string $column, |
||||||
|
string $operator, |
||||||
|
mixed $value |
||||||
|
): static |
||||||
|
{ |
||||||
|
$this->havings[] = "{$column} {$operator} ?"; |
||||||
|
|
||||||
|
$this->bindings[] = $value; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function offset(int $offset): static |
||||||
|
{ |
||||||
|
$this->offset = $offset; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
private function getRow(): array |
||||||
|
{ |
||||||
|
$stmt = $this->pdo->prepare( |
||||||
|
$this->buildSql() |
||||||
|
); |
||||||
|
|
||||||
|
$stmt->execute($this->bindings); |
||||||
|
|
||||||
|
return $stmt->fetch(\PDO::FETCH_ASSOC); |
||||||
|
} |
||||||
|
|
||||||
|
public function get() |
||||||
|
{ |
||||||
|
$stmt = $this->pdo->prepare( |
||||||
|
$this->buildSql() |
||||||
|
); |
||||||
|
|
||||||
|
$stmt->execute($this->bindings); |
||||||
|
return $stmt; |
||||||
|
} |
||||||
|
|
||||||
|
public function first(): ?array |
||||||
|
{ |
||||||
|
$this->limit(1); |
||||||
|
|
||||||
|
$rows = $this->getRow(); |
||||||
|
|
||||||
|
return $rows ?? null; |
||||||
|
} |
||||||
|
|
||||||
|
public function count(): int |
||||||
|
{ |
||||||
|
$this->select = 'COUNT(' . $this->primaryKey . ')'; |
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare( |
||||||
|
$this->buildSql() |
||||||
|
); |
||||||
|
|
||||||
|
$stmt->execute($this->bindings); |
||||||
|
|
||||||
|
return (int) $stmt->fetchColumn(); |
||||||
|
} |
||||||
|
|
||||||
|
public function sum(string $column): int |
||||||
|
{ |
||||||
|
$this->select = "SUM({$column})"; |
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare( |
||||||
|
$this->buildSql() |
||||||
|
); |
||||||
|
|
||||||
|
$stmt->execute($this->bindings); |
||||||
|
|
||||||
|
return (int) $stmt->fetchColumn(); |
||||||
|
} |
||||||
|
|
||||||
|
public function exists(): bool |
||||||
|
{ |
||||||
|
$this->limit(1); |
||||||
|
|
||||||
|
return $this->count() > 0; |
||||||
|
} |
||||||
|
|
||||||
|
public function toSql(): string |
||||||
|
{ |
||||||
|
return $this->buildSql(); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue