Lines 70.00% 14 / 20
Functions and Methods 70.00% 14 / 20
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
FiltersWhere 70.00% 14 / 20 70.00% 14 / 20 30.80 0.00% 0 / 1
 where n/a 0 / 0 n/a 0 / 0 0
 whereEq 100.00% 1 / 1 100.00% 1 / 1 1
 orWhereEq 100.00% 1 / 1 100.00% 1 / 1 1
 whereNested n/a 0 / 0 n/a 0 / 0 0
 whereNestedGroup 100.00% 1 / 1 100.00% 1 / 1 1
 orWhereNested 100.00% 1 / 1 100.00% 1 / 1 1
 whereExists n/a 0 / 0 n/a 0 / 0 0
 whereNotExists 0.00% 0 / 1 0.00% 0 / 1 2
 orWhereExists 0.00% 0 / 1 0.00% 0 / 1 2
 orWhereNotExists 0.00% 0 / 1 0.00% 0 / 1 2
 whereInQuery n/a 0 / 0 n/a 0 / 0 0
 whereNotInQuery 0.00% 0 / 1 0.00% 0 / 1 2
 orWhereInQuery 0.00% 0 / 1 0.00% 0 / 1 2
 orWhereNotInQuery 0.00% 0 / 1 0.00% 0 / 1 2
 orWhere 100.00% 1 / 1 100.00% 1 / 1 1
 whereIn 100.00% 1 / 1 100.00% 1 / 1 1
 whereNotIn 100.00% 1 / 1 100.00% 1 / 1 1
 whereNull 100.00% 1 / 1 100.00% 1 / 1 1
 whereNotNull 100.00% 1 / 1 100.00% 1 / 1 1
 whereBetween 100.00% 1 / 1 100.00% 1 / 1 1
 whereNotBetween 100.00% 1 / 1 100.00% 1 / 1 1
 whereLike 100.00% 1 / 1 100.00% 1 / 1 1
 orWhereLike 100.00% 1 / 1 100.00% 1 / 1 1
 whereNotLike 100.00% 1 / 1 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace BlueprintAU\Radiant\Concerns;
6
7use BlueprintAU\Radiant\Database\Query\Enums\WhereBoolean;
8use BlueprintAU\Radiant\Database\Query\Enums\WhereOperator;
9use BlueprintAU\Radiant\Database\Query\QueryBuilder;
10use BlueprintAU\Radiant\Database\Query\WhereBuilder;
11
12/**
13 * The shared where-family vocabulary — the one definition of every
14 * where-derived helper.
15 *
16 * Every helper funnels into one of the abstract sink members — the
17 * {@see FiltersWhere::where()} value sink plus the whereNested(),
18 * whereExists() and whereInQuery() structural sinks — so a host
19 * implementing the sinks gets the whole vocabulary for free. Hosts are
20 * immutable: every sink returns a new host, and every helper returns the
21 * sink's result directly. Column parameters accept `string|Expression`.
22 *
23 * Consumers:
24 * - {@see \BlueprintAU\Radiant\Database\Query\QueryBuilder}
25 * - {@see \BlueprintAU\Radiant\Database\Query\WhereBuilder}
26 * - {@see FiltersQuery}
27 */
28trait FiltersWhere
29{
30    /**
31     * Add a where clause — the single sink every other filter funnels into.
32     *
33     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
34     * @param  WhereOperator|string  $operator
35     * @param  mixed  $value
36     * @param  WhereBoolean  $boolean
37     * @return static
38     */
39    abstract public function where(
40        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
41        WhereOperator|string $operator,
42        mixed $value,
43        WhereBoolean $boolean = WhereBoolean::And,
44    ): static;
45
46    /**
47     * Add an equality where clause — sugar for
48     * `where($column, '=', $value)`.
49     *
50     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
51     * @param  mixed  $value
52     * @param  WhereBoolean  $boolean
53     * @return static
54     */
55    public function whereEq(
56        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
57        mixed $value,
58        WhereBoolean $boolean = WhereBoolean::And,
59    ): static {
60        return $this->where($column, WhereOperator::Eq, $value, $boolean);
61    }
62
63    /**
64     * Add an OR-connected equality where clause.
65     *
66     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
67     * @param  mixed  $value
68     * @return static
69     */
70    public function orWhereEq(
71        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
72        mixed $value,
73    ): static {
74        return $this->where($column, WhereOperator::Eq, $value, WhereBoolean::Or);
75    }
76
77    /**
78     * Add a nested where group — the second sink.
79     *
80     * @param  callable(WhereBuilder): WhereBuilder  $callback
81     * @param  WhereBoolean  $boolean
82     * @return static
83     */
84    abstract public function whereNested(
85        callable $callback,
86        WhereBoolean $boolean = WhereBoolean::And,
87    ): static;
88
89    /**
90     * Add a nested where group connected by AND.
91     *
92     * @param  callable(WhereBuilder): WhereBuilder  $callback
93     * @return static
94     */
95    public function whereNestedGroup(callable $callback): static
96    {
97        return $this->whereNested($callback, WhereBoolean::And);
98    }
99
100    /**
101     * Add an OR-connected nested where group.
102     *
103     * @param  callable(WhereBuilder): WhereBuilder  $callback
104     * @return static
105     */
106    public function orWhereNested(callable $callback): static
107    {
108        return $this->whereNested($callback, WhereBoolean::Or);
109    }
110
111    /**
112     * Add an `EXISTS (subquery)` clause to the query.
113     *
114     * The subquery is a caller-built builder, typically another model's
115     * `newQuery()` correlated to the outer query via `whereColumn()`.
116     *
117     * @param  QueryBuilder  $query  The existential subquery.
118     * @param  WhereBoolean  $boolean
119     * @param  bool  $negated  True renders `NOT EXISTS`.
120     * @return static
121     */
122    abstract public function whereExists(
123        QueryBuilder $query,
124        WhereBoolean $boolean = WhereBoolean::And,
125        bool $negated = false,
126    ): static;
127
128    /**
129     * Add a `NOT EXISTS (subquery)` clause.
130     *
131     * @param  QueryBuilder  $query  The existential subquery.
132     * @param  WhereBoolean  $boolean
133     * @return static
134     */
135    public function whereNotExists(QueryBuilder $query, WhereBoolean $boolean = WhereBoolean::And): static
136    {
137        return $this->whereExists($query, $boolean, true);
138    }
139
140    /**
141     * Add an OR-connected `EXISTS (subquery)` clause.
142     *
143     * @param  QueryBuilder  $query  The existential subquery.
144     * @return static
145     */
146    public function orWhereExists(QueryBuilder $query): static
147    {
148        return $this->whereExists($query, WhereBoolean::Or);
149    }
150
151    /**
152     * Add an OR-connected `NOT EXISTS (subquery)` clause.
153     *
154     * @param  QueryBuilder  $query  The existential subquery.
155     * @return static
156     */
157    public function orWhereNotExists(QueryBuilder $query): static
158    {
159        return $this->whereExists($query, WhereBoolean::Or, true);
160    }
161
162    /**
163     * Add a `column IN (subquery)` clause to the query.
164     *
165     * The subquery must select exactly one column.
166     *
167     * @param  string  $column  The outer column the IN constrains.
168     * @param  QueryBuilder  $query  The single-column value subquery.
169     * @param  WhereBoolean  $boolean
170     * @param  bool  $negated  True renders `NOT IN`.
171     * @return static
172     */
173    abstract public function whereInQuery(
174        string $column,
175        QueryBuilder $query,
176        WhereBoolean $boolean = WhereBoolean::And,
177        bool $negated = false,
178    ): static;
179
180    /**
181     * Add a `column NOT IN (subquery)` clause.
182     *
183     * @param  string  $column  The outer column the NOT IN constrains.
184     * @param  QueryBuilder  $query  The single-column value subquery.
185     * @param  WhereBoolean  $boolean
186     * @return static
187     */
188    public function whereNotInQuery(string $column, QueryBuilder $query, WhereBoolean $boolean = WhereBoolean::And): static
189    {
190        return $this->whereInQuery($column, $query, $boolean, true);
191    }
192
193    /**
194     * Add an OR-connected `column IN (subquery)` clause.
195     *
196     * @param  string  $column  The outer column the IN constrains.
197     * @param  QueryBuilder  $query  The single-column value subquery.
198     * @return static
199     */
200    public function orWhereInQuery(string $column, QueryBuilder $query): static
201    {
202        return $this->whereInQuery($column, $query, WhereBoolean::Or);
203    }
204
205    /**
206     * Add an OR-connected `column NOT IN (subquery)` clause.
207     *
208     * @param  string  $column  The outer column the NOT IN constrains.
209     * @param  QueryBuilder  $query  The single-column value subquery.
210     * @return static
211     */
212    public function orWhereNotInQuery(string $column, QueryBuilder $query): static
213    {
214        return $this->whereInQuery($column, $query, WhereBoolean::Or, true);
215    }
216
217    /**
218     * Add an `or where` clause.
219     *
220     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
221     * @param  WhereOperator|string  $operator
222     * @param  mixed  $value
223     * @return static
224     */
225    public function orWhere(
226        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
227        WhereOperator|string $operator,
228        mixed $value,
229    ): static {
230        return $this->where($column, $operator, $value, WhereBoolean::Or);
231    }
232
233    /**
234     * Add a `where in` clause.
235     *
236     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
237     * @param  array<int, mixed>  $values
238     * @param  WhereBoolean  $boolean
239     * @return static
240     */
241    public function whereIn(
242        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
243        array $values,
244        WhereBoolean $boolean = WhereBoolean::And,
245    ): static {
246        return $this->where($column, WhereOperator::In, $values, $boolean);
247    }
248
249    /**
250     * Add a `where not in` clause.
251     *
252     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
253     * @param  array<int, mixed>  $values
254     * @param  WhereBoolean  $boolean
255     * @return static
256     */
257    public function whereNotIn(
258        string|\BlueprintAU\Radiant\Database\Query\Expression $column,
259        array $values,
260        WhereBoolean $boolean = WhereBoolean::And,
261    ): static {
262        return $this->where($column, WhereOperator::NotIn, $values, $boolean);
263    }
264
265    /**
266     * Add a `where null` clause.
267     *
268     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
269     * @param  WhereBoolean  $boolean
270     * @return static
271     */
272    public function whereNull(string|\BlueprintAU\Radiant\Database\Query\Expression $column, WhereBoolean $boolean = WhereBoolean::And): static
273    {
274        return $this->where($column, WhereOperator::Null, null, $boolean);
275    }
276
277    /**
278     * Add a `where not null` clause.
279     *
280     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
281     * @param  WhereBoolean  $boolean
282     * @return static
283     */
284    public function whereNotNull(string|\BlueprintAU\Radiant\Database\Query\Expression $column, WhereBoolean $boolean = WhereBoolean::And): static
285    {
286        return $this->where($column, WhereOperator::NotNull, null, $boolean);
287    }
288
289    /**
290     * Add a `where between` clause.
291     *
292     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
293     * @param  array{0: mixed, 1: mixed}  $range
294     * @param  WhereBoolean  $boolean
295     * @return static
296     */
297    public function whereBetween(string|\BlueprintAU\Radiant\Database\Query\Expression $column, array $range, WhereBoolean $boolean = WhereBoolean::And): static
298    {
299        return $this->where($column, WhereOperator::Between, $range, $boolean);
300    }
301
302    /**
303     * Add a `where not between` clause.
304     *
305     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
306     * @param  array{0: mixed, 1: mixed}  $range
307     * @param  WhereBoolean  $boolean
308     * @return static
309     */
310    public function whereNotBetween(string|\BlueprintAU\Radiant\Database\Query\Expression $column, array $range, WhereBoolean $boolean = WhereBoolean::And): static
311    {
312        return $this->where($column, WhereOperator::NotBetween, $range, $boolean);
313    }
314
315    /**
316     * Add a `where like` clause — the pattern is a bound value.
317     *
318     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
319     * @param  string  $pattern
320     * @param  WhereBoolean  $boolean
321     * @return static
322     */
323    public function whereLike(string|\BlueprintAU\Radiant\Database\Query\Expression $column, string $pattern, WhereBoolean $boolean = WhereBoolean::And): static
324    {
325        return $this->where($column, WhereOperator::Like, $pattern, $boolean);
326    }
327
328    /**
329     * Add an OR-connected `where like` clause.
330     *
331     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
332     * @param  string  $pattern
333     * @return static
334     */
335    public function orWhereLike(string|\BlueprintAU\Radiant\Database\Query\Expression $column, string $pattern): static
336    {
337        return $this->where($column, WhereOperator::Like, $pattern, WhereBoolean::Or);
338    }
339
340    /**
341     * Add a `where not like` clause.
342     *
343     * @param  string|\BlueprintAU\Radiant\Database\Query\Expression  $column
344     * @param  string  $pattern
345     * @param  WhereBoolean  $boolean
346     * @return static
347     */
348    public function whereNotLike(string|\BlueprintAU\Radiant\Database\Query\Expression $column, string $pattern, WhereBoolean $boolean = WhereBoolean::And): static
349    {
350        return $this->where($column, WhereOperator::NotLike, $pattern, $boolean);
351    }
352}