pdo = $pdo; } public function table(string $table): static { $this->table = $table; return $this; } public function select(array|string $columns): static { $this->select = is_array($columns) ? $columns : explode(',', $columns); return $this; } public function where(string $column, string $operator = "", mixed $value = ""): static { $placeholder = ':w_' . count($this->bindings); $this->where[] = "$column $operator $placeholder"; $this->bindings[$placeholder] = $value; return $this; } public function orderBy(string $column, string $direction = 'ASC'): static { $this->order[] = "$column $direction"; return $this; } public function limit(int $limit): static { $this->limit = $limit; return $this; } public function offset(int $offset): static { $this->offset = $offset; return $this; } public function when(bool $condition, callable $callback): static { if ($condition) { $callback($this); } return $this; } public function rawWhere(string $expression, array $bindings = []): static { $this->where[] = "($expression)"; foreach ($bindings as $key => $value) { $this->bindings[":raw_" . $key] = $value; } return $this; } public function get(): array { $sql = "SELECT " . implode(', ', $this->select) . " FROM {$this->table}"; if (!empty($this->where)) { $sql .= " WHERE " . implode(' AND ', $this->where); } if (!empty($this->order)) { $sql .= " ORDER BY " . implode(', ', $this->order); } if (!is_null($this->limit)) { $sql .= " LIMIT :__limit"; $this->bindings[':__limit'] = $this->limit; } if (!is_null($this->offset)) { $sql .= " OFFSET :__offset"; $this->bindings[':__offset'] = $this->offset; } $stmt = $this->pdo->prepare($sql); foreach ($this->bindings as $placeholder => $value) { $stmt->bindValue($placeholder, $value); } $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function first(): ?array { $this->limit(1); $rows = $this->get(); return $rows[0] ?? null; } public function count(): int { $this->select = ['COUNT(*) AS cnt']; $results = $this->get(); return (int)($results[0]['cnt'] ?? 0); } public function exists(): bool { return $this->count() > 0; } public function insert(array $data): bool { $columns = array_keys($data); $placeholders = array_map(fn($col) => ':' . $col, $columns); $sql = "INSERT INTO {$this->table} (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")"; $stmt = $this->pdo->prepare($sql); foreach ($data as $col => $value) { $stmt->bindValue(':' . $col, $value); } return $stmt->execute(); } public function update(array $data): bool { if (empty($this->where)) { throw new \Exception("Update without WHERE is not allowed."); } $setClauses = []; foreach ($data as $col => $val) { $placeholder = ':u_' . $col; $setClauses[] = "$col = $placeholder"; $this->bindings[$placeholder] = $val; } $sql = "UPDATE {$this->table} SET " . implode(', ', $setClauses); if (!empty($this->where)) { $sql .= " WHERE " . implode(' AND ', $this->where); } $stmt = $this->pdo->prepare($sql); foreach ($this->bindings as $placeholder => $value) { $stmt->bindValue($placeholder, $value); } return $stmt->execute(); } public function delete(): bool { if (empty($this->where)) { throw new \Exception("Delete without WHERE is not allowed."); } $sql = "DELETE FROM {$this->table} WHERE " . implode(' AND ', $this->where); $stmt = $this->pdo->prepare($sql); foreach ($this->bindings as $placeholder => $value) { $stmt->bindValue($placeholder, $value); } return $stmt->execute(); } public function whereIn(string $column, array $values, string $boolean = 'AND'): static { if (empty($values)) { throw new \InvalidArgumentException("whereIn: values 배열이 비어있을 수 없습니다."); } $placeholders = implode(', ', array_fill(0, count($values), '?')); $this->where[] = "$column IN ($placeholders)"; $this->bindings = array_merge($this->bindings, $values); return $this; } public function whereNotIn(string $column, array $values, string $boolean = 'AND'): static { if (empty($values)) { throw new \InvalidArgumentException("whereNotIn: values 배열이 비어있을 수 없습니다."); } $placeholders = implode(', ', array_fill(0, count($values), '?')); $this->where[] = "$column NOT IN ($placeholders)"; $this->bindings = array_merge($this->bindings, $values); return $this; } public function orWhereIn(string $column, array $values): static { return $this->whereIn($column, $values, 'OR'); } public function orWhere(string $column, string $operator, mixed $value): static { $placeholder = ':w_' . count($this->bindings); $condition = "$column $operator $placeholder"; if (empty($this->where)) { $this->where[] = $condition; } else { $last = array_pop($this->where); $this->where[] = "($last OR $condition)"; } $this->bindings[$placeholder] = $value; return $this; } public function join(string $table, string $on, string $type = 'INNER'): static { $this->joins[] = strtoupper($type) . " JOIN $table ON $on"; return $this; } public function raw(string $sql, array $bindings = []): array { $stmt = $this->pdo->prepare($sql); foreach ($bindings as $key => $value) { $stmt->bindValue(is_int($key) ? $key + 1 : $key, $value); } $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function beginTransaction(): void { $this->pdo->beginTransaction(); } public function commit(): void { $this->pdo->commit(); } public function rollBack(): void { if ($this->pdo->inTransaction()) { $this->pdo->rollBack(); } } }