feat: Complete Woles Framework v1.0 with enterprise-grade UI
- Add comprehensive error handling system with custom error pages - Implement professional enterprise-style design with Tailwind CSS - Create modular HMVC architecture with clean separation of concerns - Add security features: CSRF protection, XSS filtering, Argon2ID hashing - Include CLI tools for development workflow - Add error reporting dashboard with system monitoring - Implement responsive design with consistent slate color scheme - Replace all emoji icons with professional SVG icons - Add comprehensive test suite with PHPUnit - Include database migrations and seeders - Add proper exception handling with fallback pages - Implement template engine with custom syntax support - Add helper functions and facades for clean code - Include proper logging and debugging capabilities
This commit is contained in:
443
app/Core/Database/QueryBuilder.php
Normal file
443
app/Core/Database/QueryBuilder.php
Normal file
@@ -0,0 +1,443 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core\Database;
|
||||
|
||||
/**
|
||||
* NovaCore Query Builder
|
||||
* Simple query builder for database operations
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
private Connection $connection;
|
||||
private string $table;
|
||||
private array $select = ['*'];
|
||||
private array $where = [];
|
||||
private array $orderBy = [];
|
||||
private array $groupBy = [];
|
||||
private array $having = [];
|
||||
private ?int $limit = null;
|
||||
private ?int $offset = null;
|
||||
private array $joins = [];
|
||||
|
||||
public function __construct(Connection $connection, string $table)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select columns
|
||||
*/
|
||||
public function select(array $columns): self
|
||||
{
|
||||
$this->select = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add where condition
|
||||
*/
|
||||
public function where(string $column, $operator, $value = null): self
|
||||
{
|
||||
if ($value === null) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
}
|
||||
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => $operator,
|
||||
'value' => $value,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add OR where condition
|
||||
*/
|
||||
public function orWhere(string $column, $operator, $value = null): self
|
||||
{
|
||||
if ($value === null) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
}
|
||||
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => $operator,
|
||||
'value' => $value,
|
||||
'boolean' => 'OR'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add where in condition
|
||||
*/
|
||||
public function whereIn(string $column, array $values): self
|
||||
{
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => 'IN',
|
||||
'value' => $values,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add where not in condition
|
||||
*/
|
||||
public function whereNotIn(string $column, array $values): self
|
||||
{
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => 'NOT IN',
|
||||
'value' => $values,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add where null condition
|
||||
*/
|
||||
public function whereNull(string $column): self
|
||||
{
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => 'IS NULL',
|
||||
'value' => null,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add where not null condition
|
||||
*/
|
||||
public function whereNotNull(string $column): self
|
||||
{
|
||||
$this->where[] = [
|
||||
'column' => $column,
|
||||
'operator' => 'IS NOT NULL',
|
||||
'value' => null,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add order by clause
|
||||
*/
|
||||
public function orderBy(string $column, string $direction = 'ASC'): self
|
||||
{
|
||||
$this->orderBy[] = [
|
||||
'column' => $column,
|
||||
'direction' => strtoupper($direction)
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add group by clause
|
||||
*/
|
||||
public function groupBy(string $column): self
|
||||
{
|
||||
$this->groupBy[] = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add having clause
|
||||
*/
|
||||
public function having(string $column, $operator, $value): self
|
||||
{
|
||||
$this->having[] = [
|
||||
'column' => $column,
|
||||
'operator' => $operator,
|
||||
'value' => $value,
|
||||
'boolean' => 'AND'
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add limit clause
|
||||
*/
|
||||
public function limit(int $limit): self
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add offset clause
|
||||
*/
|
||||
public function offset(int $offset): self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add join clause
|
||||
*/
|
||||
public function join(string $table, string $first, string $operator, string $second, string $type = 'INNER'): self
|
||||
{
|
||||
$this->joins[] = [
|
||||
'table' => $table,
|
||||
'first' => $first,
|
||||
'operator' => $operator,
|
||||
'second' => $second,
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add left join clause
|
||||
*/
|
||||
public function leftJoin(string $table, string $first, string $operator, string $second): self
|
||||
{
|
||||
return $this->join($table, $first, $operator, $second, 'LEFT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add right join clause
|
||||
*/
|
||||
public function rightJoin(string $table, string $first, string $operator, string $second): self
|
||||
{
|
||||
return $this->join($table, $first, $operator, $second, 'RIGHT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all results
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$sql = $this->toSql();
|
||||
$params = $this->getBindings();
|
||||
|
||||
return $this->connection->fetchAll($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first result
|
||||
*/
|
||||
public function first(): ?array
|
||||
{
|
||||
$this->limit(1);
|
||||
$sql = $this->toSql();
|
||||
$params = $this->getBindings();
|
||||
|
||||
return $this->connection->fetch($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$this->select = ['COUNT(*) as count'];
|
||||
$result = $this->first();
|
||||
return (int) $result['count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert data
|
||||
*/
|
||||
public function insert(array $data): int
|
||||
{
|
||||
$columns = array_keys($data);
|
||||
$values = array_values($data);
|
||||
$placeholders = array_fill(0, count($values), '?');
|
||||
|
||||
$sql = "INSERT INTO {$this->table} (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";
|
||||
|
||||
return $this->connection->execute($sql, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data
|
||||
*/
|
||||
public function update(array $data): int
|
||||
{
|
||||
$columns = array_keys($data);
|
||||
$values = array_values($data);
|
||||
$set = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$set[] = "{$column} = ?";
|
||||
}
|
||||
|
||||
$sql = "UPDATE {$this->table} SET " . implode(', ', $set);
|
||||
$params = $values;
|
||||
|
||||
if (!empty($this->where)) {
|
||||
$sql .= " WHERE " . $this->buildWhereClause();
|
||||
$params = array_merge($params, $this->getWhereBindings());
|
||||
}
|
||||
|
||||
return $this->connection->execute($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete records
|
||||
*/
|
||||
public function delete(): int
|
||||
{
|
||||
$sql = "DELETE FROM {$this->table}";
|
||||
$params = [];
|
||||
|
||||
if (!empty($this->where)) {
|
||||
$sql .= " WHERE " . $this->buildWhereClause();
|
||||
$params = $this->getWhereBindings();
|
||||
}
|
||||
|
||||
return $this->connection->execute($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL query
|
||||
*/
|
||||
private function toSql(): string
|
||||
{
|
||||
$sql = "SELECT " . implode(', ', $this->select) . " FROM {$this->table}";
|
||||
|
||||
// Add joins
|
||||
foreach ($this->joins as $join) {
|
||||
$sql .= " {$join['type']} JOIN {$join['table']} ON {$join['first']} {$join['operator']} {$join['second']}";
|
||||
}
|
||||
|
||||
// Add where clause
|
||||
if (!empty($this->where)) {
|
||||
$sql .= " WHERE " . $this->buildWhereClause();
|
||||
}
|
||||
|
||||
// Add group by clause
|
||||
if (!empty($this->groupBy)) {
|
||||
$sql .= " GROUP BY " . implode(', ', $this->groupBy);
|
||||
}
|
||||
|
||||
// Add having clause
|
||||
if (!empty($this->having)) {
|
||||
$sql .= " HAVING " . $this->buildHavingClause();
|
||||
}
|
||||
|
||||
// Add order by clause
|
||||
if (!empty($this->orderBy)) {
|
||||
$orderBy = [];
|
||||
foreach ($this->orderBy as $order) {
|
||||
$orderBy[] = "{$order['column']} {$order['direction']}";
|
||||
}
|
||||
$sql .= " ORDER BY " . implode(', ', $orderBy);
|
||||
}
|
||||
|
||||
// Add limit clause
|
||||
if ($this->limit !== null) {
|
||||
$sql .= " LIMIT {$this->limit}";
|
||||
}
|
||||
|
||||
// Add offset clause
|
||||
if ($this->offset !== null) {
|
||||
$sql .= " OFFSET {$this->offset}";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build where clause
|
||||
*/
|
||||
private function buildWhereClause(): string
|
||||
{
|
||||
$clauses = [];
|
||||
|
||||
foreach ($this->where as $index => $condition) {
|
||||
$clause = '';
|
||||
|
||||
if ($index > 0) {
|
||||
$clause .= " {$condition['boolean']} ";
|
||||
}
|
||||
|
||||
if ($condition['operator'] === 'IN' || $condition['operator'] === 'NOT IN') {
|
||||
$placeholders = array_fill(0, count($condition['value']), '?');
|
||||
$clause .= "{$condition['column']} {$condition['operator']} (" . implode(', ', $placeholders) . ")";
|
||||
} else {
|
||||
$clause .= "{$condition['column']} {$condition['operator']} ?";
|
||||
}
|
||||
|
||||
$clauses[] = $clause;
|
||||
}
|
||||
|
||||
return implode('', $clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build having clause
|
||||
*/
|
||||
private function buildHavingClause(): string
|
||||
{
|
||||
$clauses = [];
|
||||
|
||||
foreach ($this->having as $index => $condition) {
|
||||
$clause = '';
|
||||
|
||||
if ($index > 0) {
|
||||
$clause .= " {$condition['boolean']} ";
|
||||
}
|
||||
|
||||
$clause .= "{$condition['column']} {$condition['operator']} ?";
|
||||
$clauses[] = $clause;
|
||||
}
|
||||
|
||||
return implode('', $clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bindings
|
||||
*/
|
||||
private function getBindings(): array
|
||||
{
|
||||
$bindings = [];
|
||||
|
||||
// Add where bindings
|
||||
$bindings = array_merge($bindings, $this->getWhereBindings());
|
||||
|
||||
// Add having bindings
|
||||
foreach ($this->having as $condition) {
|
||||
$bindings[] = $condition['value'];
|
||||
}
|
||||
|
||||
return $bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where bindings
|
||||
*/
|
||||
private function getWhereBindings(): array
|
||||
{
|
||||
$bindings = [];
|
||||
|
||||
foreach ($this->where as $condition) {
|
||||
if ($condition['operator'] === 'IN' || $condition['operator'] === 'NOT IN') {
|
||||
$bindings = array_merge($bindings, $condition['value']);
|
||||
} else {
|
||||
$bindings[] = $condition['value'];
|
||||
}
|
||||
}
|
||||
|
||||
return $bindings;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user