You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -32,6 +32,7 @@ decorator that makes it easier to build your query in shared contexts.
32
32
-[Examples](#examples)
33
33
-[A real world case](#a-real-world-case)
34
34
-[That's why SharedQueryBuilder is going to save your ass in these situations](#thats-why-sharedquerybuilder-is-going-to-save-your-ass-in-these-situations)
35
+
-[Evolution: filters that receive and return a Proposal](#evolution-filters-that-receive-and-return-a-proposal)
35
36
-[Immutable Parameters](#immutable-parameters)
36
37
-[Set parameter and use it in expression at the same moment](#set-parameter-and-use-it-in-expression-at-the-same-moment)
A proposal exposes the same method names as the SQB for building a **local** set of conditions, joins, parameters, and other parts. Nothing is written to the main query until the proposal is used in an expression.
204
+
A proposal exposes the same method names as the SQB for building a **local** set of conditions, joins, parameters, and other parts. Nothing is written to the main query until the proposal is used in an expression. Build conditions with the proposal’s `expr()` (e.g. `eq`, `neq`, `andX`, `orX`) so they stay object-oriented. **Never hardcode entity aliases**—use the SQB’s `withAlias(Entity::class, 'property')` (or the proposal’s, which delegates to the SQB) so the library resolves the correct alias.
-**Parameters**: use only `withUniqueImmutableParameter` on the proposal; on expansion, parameter names are made unique on the main SQB and the condition DQL is updated accordingly.
@@ -227,25 +240,37 @@ To merge a proposal into the main query, pass it to `andWhere`, `orWhere`, `wher
// Now the main query has the proposal’s condition and parameter; its joins/select/etc. would be applied too if we had added any.
234
252
```
235
253
236
-
You can combine multiple proposals in an OR (or AND) by expanding them first and passing the resulting strings to `expr()->orX()` (or `expr()->andX()`):
254
+
You can combine multiple proposals in an OR (or AND) by passing the proposals directly to `expr()->orX()` (or `expr()->andX()`). The SQB expands them when building the where clause; you do not need to call `expandInto()`.
#### Example: filter receives a proposal, fills it, returns it; merge at upper level
262
287
263
-
A filter class can build a proposal and the controller merges it in one place:
288
+
The **caller** (e.g. controller) creates an empty proposal and passes it to the filter. The filter **receives** the request and that proposal; it fills the proposal using the proposal’s methods (e.g. `withAlias(Entity::class, 'property')`, which delegates to the SQB) so aliases are never hardcoded, then **returns** the same proposal. The caller is responsible for merging the proposal into the query. That way the filter only builds its block of logic; where and how it is combined (e.g. `andWhere` vs `orWhere`) stays at the upper level.
264
289
265
290
```php
266
291
// StatusFilter.php
292
+
use Andante\Doctrine\ORM\SharedQueryBuilder\Proposal;
293
+
use Symfony\Component\HttpFoundation\Request;
294
+
267
295
class StatusFilter implements FilterInterface
268
296
{
269
-
public function apply(SharedQueryBuilder $sqb, Request $request): void
297
+
public function buildProposal(Request $request, Proposal $proposal): Proposal
This keeps the filter responsible only for building its conditions and parameters (using `withAlias` so the library resolves entity aliases); the caller decides how to merge and avoids alias or parameter name clashes between filters.
285
325
286
326
## Examples
287
327
@@ -293,11 +333,11 @@ There is no need to perform any join until we decide to use that filter. We can
@@ -503,6 +543,68 @@ class BuildingNameFilter implements FilterInterface
503
543
}
504
544
```
505
545
546
+
- 👍 No extra join statements executed when there is no need for them;
547
+
548
+
#### Evolution: filters that receive and return a Proposal
549
+
550
+
You can go one step further: have each filter **receive an empty Proposal** (created by the caller), fill it using `withAlias()` and the proposal’s `expr()`, and **return** that proposal. The controller then merges each proposal (e.g. with `andWhere`) at the upper level. That keeps the same benefits (no hardcoded aliases, merge logic in one place) and makes each filter a pure “block builder” that never touches the SQB’s where clause directly.
551
+
552
+
**Step 1: BuildingNameFilter receives and returns a Proposal**
553
+
554
+
```php
555
+
// BuildingNameFilter.php
556
+
use Andante\Doctrine\ORM\SharedQueryBuilder\Proposal;
557
+
use Symfony\Component\HttpFoundation\Request;
558
+
559
+
class BuildingNameFilter implements FilterInterface
560
+
{
561
+
public function buildProposal(Request $request, Proposal $proposal): Proposal
0 commit comments