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; } }