Skip to content

Commit c83888f

Browse files
committed
Implement Query Builder Traits for Conditions, Debugging, Grouping, Joins, and SQL Building
- Added QueryConditionsTrait for managing WHERE, HAVING, and other conditions in SQL queries. - Introduced QueryDebugTrait for debugging SQL queries, including methods for displaying SQL and bindings. - Created QueryGroupingTrait to handle GROUP BY, ORDER BY, LIMIT, and OFFSET clauses. - Developed QueryJoinsTrait for managing JOIN operations in SQL queries. - Implemented SqlBuilderTrait for constructing various SQL query types (SELECT, INSERT, UPDATE, DELETE) and handling WHERE clauses.
1 parent e6910cc commit c83888f

8 files changed

+1542
-957
lines changed

src/QueryBuilder/AsyncQueryBuilder.php

Lines changed: 14 additions & 957 deletions
Large diffs are not rendered by default.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
namespace Rcalicdan\FiberAsync\QueryBuilder\Traits;
4+
5+
trait QueryAdvancedConditionsTrait
6+
{
7+
/**
8+
* Add a custom condition group with specific logic.
9+
*
10+
* @param callable(static): void $callback Callback function that receives a new query builder instance.
11+
* @param string $logicalOperator How this group connects to others ('AND' or 'OR').
12+
* @return static Returns a new query builder instance for method chaining.
13+
*/
14+
public function whereGroup(callable $callback, string $logicalOperator = 'AND'): static
15+
{
16+
$subBuilder = new static($this->table);
17+
$callback($subBuilder);
18+
19+
$subSql = $subBuilder->buildWhereClause();
20+
21+
if ($subSql === '') {
22+
return $this;
23+
}
24+
25+
return $this->whereRaw("({$subSql})", $subBuilder->getCompiledBindings(), $logicalOperator);
26+
}
27+
28+
/**
29+
* Add nested WHERE conditions with custom logic.
30+
*
31+
* @param callable(static): void $callback Callback function for nested conditions.
32+
* @param string $operator How to connect with existing conditions.
33+
* @return static Returns a new query builder instance for method chaining.
34+
*/
35+
public function whereNested(callable $callback, string $operator = 'AND'): static
36+
{
37+
return $this->whereGroup($callback, $operator);
38+
}
39+
40+
/**
41+
* Add a nested OR WHERE condition with custom logic.
42+
*
43+
* @param callable(static): void $callback Callback function for nested conditions.
44+
* @return static Returns a new query builder instance for method chaining.
45+
*/
46+
public function orWhereNested(callable $callback): static
47+
{
48+
return $this->whereGroup($callback, 'OR');
49+
}
50+
51+
/**
52+
* Add conditions with EXISTS clause.
53+
*
54+
* @param callable(static): void $callback Callback function for the EXISTS subquery.
55+
* @param string $operator Logical operator ('AND' or 'OR').
56+
* @return static Returns a new query builder instance for method chaining.
57+
*/
58+
public function whereExists(callable $callback, string $operator = 'AND'): static
59+
{
60+
$subBuilder = new static;
61+
$callback($subBuilder);
62+
63+
$subSql = $subBuilder->buildSelectQuery();
64+
$condition = "EXISTS ({$subSql})";
65+
66+
return $this->whereRaw($condition, $subBuilder->getCompiledBindings(), $operator);
67+
}
68+
69+
/**
70+
* Add conditions with NOT EXISTS clause.
71+
*
72+
* @param callable(static): void $callback Callback function for the NOT EXISTS subquery.
73+
* @param string $operator Logical operator ('AND' or 'OR').
74+
* @return static Returns a new query builder instance for method chaining.
75+
*/
76+
public function whereNotExists(callable $callback, string $operator = 'AND'): static
77+
{
78+
$subBuilder = new static;
79+
$callback($subBuilder);
80+
81+
$subSql = $subBuilder->buildSelectQuery();
82+
$condition = "NOT EXISTS ({$subSql})";
83+
84+
return $this->whereRaw($condition, $subBuilder->getCompiledBindings(), $operator);
85+
}
86+
87+
/**
88+
* Add an OR WHERE EXISTS clause.
89+
*
90+
* @param callable(static): void $callback Callback function for the EXISTS subquery.
91+
* @return static Returns a new query builder instance for method chaining.
92+
*/
93+
public function orWhereExists(callable $callback): static
94+
{
95+
return $this->whereExists($callback, 'OR');
96+
}
97+
98+
/**
99+
* Add an OR WHERE NOT EXISTS clause.
100+
*
101+
* @param callable(static): void $callback Callback function for the NOT EXISTS subquery.
102+
* @return static Returns a new query builder instance for method chaining.
103+
*/
104+
public function orWhereNotExists(callable $callback): static
105+
{
106+
return $this->whereNotExists($callback, 'OR');
107+
}
108+
109+
/**
110+
* Add a WHERE clause with a subquery.
111+
*
112+
* @param string $column The column name.
113+
* @param string $operator The comparison operator.
114+
* @param callable(static): void $callback Callback function for the subquery.
115+
* @return static Returns a new query builder instance for method chaining.
116+
*/
117+
public function whereSub(string $column, string $operator, callable $callback): static
118+
{
119+
$subBuilder = new static;
120+
$callback($subBuilder);
121+
122+
$subSql = $subBuilder->buildSelectQuery();
123+
$condition = "{$column} {$operator} ({$subSql})";
124+
125+
return $this->whereRaw($condition, $subBuilder->getCompiledBindings());
126+
}
127+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
namespace Rcalicdan\FiberAsync\QueryBuilder\Traits;
4+
5+
trait QueryBuilderCoreTrait
6+
{
7+
/**
8+
* @var string The table name for the query.
9+
*/
10+
protected string $table = '';
11+
12+
/**
13+
* @var array<string> The columns to select in the query.
14+
*/
15+
protected array $select = ['*'];
16+
17+
/**
18+
* @var array<string, array<mixed>> The parameter bindings for the query, grouped by type.
19+
*/
20+
protected array $bindings = [
21+
'where' => [],
22+
'whereIn' => [],
23+
'whereNotIn' => [],
24+
'whereBetween' => [],
25+
'whereRaw' => [],
26+
'orWhere' => [],
27+
'orWhereRaw' => [],
28+
'having' => [],
29+
];
30+
31+
/**
32+
* @var int The current binding index counter.
33+
*/
34+
protected int $bindingIndex = 0;
35+
36+
/**
37+
* Set the table for the query.
38+
*
39+
* @param string $table The table name.
40+
* @return static Returns a new query builder instance for method chaining.
41+
*/
42+
public function table(string $table): static
43+
{
44+
$instance = clone $this;
45+
$instance->table = $table;
46+
47+
return $instance;
48+
}
49+
50+
/**
51+
* Set the columns to select.
52+
*
53+
* @param string|array<string> $columns The columns to select.
54+
* @return static Returns a new query builder instance for method chaining.
55+
*/
56+
public function select(string|array $columns = '*'): static
57+
{
58+
$instance = clone $this;
59+
if (is_string($columns)) {
60+
$columns = array_map('trim', explode(',', $columns));
61+
}
62+
$instance->select = $columns;
63+
64+
return $instance;
65+
}
66+
67+
/**
68+
* Add columns to the existing select.
69+
*
70+
* @param string|array<string> $columns The columns to add.
71+
* @return static Returns a new query builder instance for method chaining.
72+
*/
73+
public function addSelect(string|array $columns): static
74+
{
75+
$instance = clone $this;
76+
if (is_string($columns)) {
77+
$columns = array_map('trim', explode(',', $columns));
78+
}
79+
$instance->select = array_merge($instance->select, $columns);
80+
81+
return $instance;
82+
}
83+
84+
/**
85+
* Select distinct records.
86+
*
87+
* @param string|array<string> $columns The columns to select.
88+
* @return static Returns a new query builder instance for method chaining.
89+
*/
90+
public function selectDistinct(string|array $columns = '*'): static
91+
{
92+
$instance = $this->select($columns);
93+
94+
// Add DISTINCT to the first column
95+
if ($instance->select !== [] && $instance->select[0] !== '*') {
96+
$instance->select[0] = 'DISTINCT ' . $instance->select[0];
97+
} elseif ($instance->select[0] === '*') {
98+
$instance->select[0] = 'DISTINCT *';
99+
}
100+
101+
return $instance;
102+
}
103+
104+
/**
105+
* Generate a parameter placeholder for prepared statements.
106+
*
107+
* @return string The placeholder string.
108+
*/
109+
protected function getPlaceholder(): string
110+
{
111+
return '?';
112+
}
113+
114+
/**
115+
* Compiles the final bindings array in the correct order for execution.
116+
*
117+
* @return array<mixed>
118+
*/
119+
protected function getCompiledBindings(): array
120+
{
121+
// This merge order MUST match the order in `collectAllConditionParts()`
122+
$whereBindings = array_merge(
123+
$this->bindings['where'],
124+
$this->bindings['whereIn'],
125+
$this->bindings['whereNotIn'],
126+
$this->bindings['whereBetween'],
127+
$this->bindings['whereRaw'],
128+
$this->bindings['orWhere'],
129+
$this->bindings['orWhereRaw']
130+
);
131+
132+
return array_merge($whereBindings, $this->bindings['having']);
133+
}
134+
}

0 commit comments

Comments
 (0)