Skip to content

Commit 7852778

Browse files
GromNaNalcaeus
authored andcommitted
PHPORM-49 Implement Query\Builder::whereNot by encapsulating into $not (#13)
`Query\Builder::whereNot` was simply ignoring the "not" and breaking the built query.
1 parent f729baa commit 7852778

File tree

4 files changed

+210
-7
lines changed

4 files changed

+210
-7
lines changed

Diff for: CHANGELOG.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ All notable changes to this project will be documented in this file.
33

44
## [Unreleased]
55

6+
- Add classes to cast ObjectId and UUID instances [#1](https://github.com/GromNaN/laravel-mongodb-private/pull/1) by [@alcaeus](https://github.com/alcaeus).
7+
- Add `Query\Builder::toMql()` to simplify comprehensive query tests [#6](https://github.com/GromNaN/laravel-mongodb-private/pull/6) by [@GromNaN](https://github.com/GromNaN).
8+
- Fix `Query\Builder::whereNot` to use MongoDB [`$not`](https://www.mongodb.com/docs/manual/reference/operator/query/not/) operator [#13](https://github.com/GromNaN/laravel-mongodb-private/pull/13) by [@GromNaN](https://github.com/GromNaN).
9+
- Fix `Query\Builder::whereBetween` to accept `Carbon\Period` object [#10](https://github.com/GromNaN/laravel-mongodb-private/pull/10) by [@GromNaN](https://github.com/GromNaN).
10+
- Throw an exception for unsupported `Query\Builder` methods [#9](https://github.com/GromNaN/laravel-mongodb-private/pull/9) by [@GromNaN](https://github.com/GromNaN).
11+
- Throw an exception when `Query\Builder::orderBy()` is used with invalid direction [#7](https://github.com/GromNaN/laravel-mongodb-private/pull/7) by [@GromNaN](https://github.com/GromNaN).
12+
- Throw an exception when `Query\Builder::push()` is used incorrectly [#5](https://github.com/GromNaN/laravel-mongodb-private/pull/5) by [@GromNaN](https://github.com/GromNaN).
13+
614
## [3.9.2] - 2022-09-01
715

8-
### Addded
16+
### Added
917
- Add single word name mutators [#2438](https://github.com/jenssegers/laravel-mongodb/pull/2438) by [@RosemaryOrchard](https://github.com/RosemaryOrchard) & [@mrneatly](https://github.com/mrneatly).
1018

1119
### Fixed

Diff for: README.md

+6
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@ $users =
332332
->get();
333333
```
334334

335+
**NOT statements**
336+
337+
```php
338+
$users = User::whereNot('age', '>', 18)->get();
339+
```
340+
335341
**whereIn**
336342

337343
```php

Diff for: src/Query/Builder.php

+13-6
Original file line numberDiff line numberDiff line change
@@ -1019,19 +1019,26 @@ protected function compileWheres(): array
10191019
}
10201020
}
10211021

1022-
// The next item in a "chain" of wheres devices the boolean of the
1023-
// first item. So if we see that there are multiple wheres, we will
1024-
// use the operator of the next where.
1025-
if ($i == 0 && count($wheres) > 1 && $where['boolean'] == 'and') {
1026-
$where['boolean'] = $wheres[$i + 1]['boolean'];
1022+
// In a sequence of "where" clauses, the logical operator of the
1023+
// first "where" is determined by the 2nd "where".
1024+
// $where['boolean'] = "and", "or", "and not" or "or not"
1025+
if ($i == 0 && count($wheres) > 1
1026+
&& str_starts_with($where['boolean'], 'and')
1027+
&& str_starts_with($wheres[$i + 1]['boolean'], 'or')
1028+
) {
1029+
$where['boolean'] = 'or'.(str_ends_with($where['boolean'], 'not') ? ' not' : '');
10271030
}
10281031

10291032
// We use different methods to compile different wheres.
10301033
$method = "compileWhere{$where['type']}";
10311034
$result = $this->{$method}($where);
10321035

1036+
if (str_ends_with($where['boolean'], 'not')) {
1037+
$result = ['$not' => $result];
1038+
}
1039+
10331040
// Wrap the where with an $or operator.
1034-
if ($where['boolean'] == 'or') {
1041+
if (str_starts_with($where['boolean'], 'or')) {
10351042
$result = ['$or' => [$result]];
10361043
}
10371044

Diff for: tests/Query/BuilderTest.php

+182
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ public static function provideQueryBuilderToMql(): iterable
5050
fn (Builder $builder) => $builder->where('foo', 'bar'),
5151
];
5252

53+
yield 'where with single array of conditions' => [
54+
['find' => [
55+
['$and' => [
56+
['foo' => 1],
57+
['bar' => 2],
58+
]],
59+
[], // options
60+
]],
61+
fn (Builder $builder) => $builder->where(['foo' => 1, 'bar' => 2]),
62+
];
63+
5364
yield 'find > date' => [
5465
['find' => [['foo' => ['$gt' => new UTCDateTime($date)]], []]],
5566
fn (Builder $builder) => $builder->where('foo', '>', $date),
@@ -65,6 +76,177 @@ public static function provideQueryBuilderToMql(): iterable
6576
fn (Builder $builder) => $builder->limit(10)->offset(5)->select('foo', 'bar'),
6677
];
6778

79+
/** @see DatabaseQueryBuilderTest::testBasicWhereNot() */
80+
yield 'whereNot (multiple)' => [
81+
['find' => [
82+
['$and' => [
83+
['$not' => ['name' => 'foo']],
84+
['$not' => ['name' => ['$ne' => 'bar']]],
85+
]],
86+
[], // options
87+
]],
88+
fn (Builder $builder) => $builder
89+
->whereNot('name', 'foo')
90+
->whereNot('name', '<>', 'bar'),
91+
];
92+
93+
/** @see DatabaseQueryBuilderTest::testBasicOrWheres() */
94+
yield 'where orWhere' => [
95+
['find' => [
96+
['$or' => [
97+
['id' => 1],
98+
['email' => 'foo'],
99+
]],
100+
[], // options
101+
]],
102+
fn (Builder $builder) => $builder
103+
->where('id', '=', 1)
104+
->orWhere('email', '=', 'foo'),
105+
];
106+
107+
/** @see DatabaseQueryBuilderTest::testBasicOrWhereNot() */
108+
yield 'orWhereNot' => [
109+
['find' => [
110+
['$or' => [
111+
['$not' => ['name' => 'foo']],
112+
['$not' => ['name' => ['$ne' => 'bar']]],
113+
]],
114+
[], // options
115+
]],
116+
fn (Builder $builder) => $builder
117+
->orWhereNot('name', 'foo')
118+
->orWhereNot('name', '<>', 'bar'),
119+
];
120+
121+
yield 'whereNot orWhere' => [
122+
['find' => [
123+
['$or' => [
124+
['$not' => ['name' => 'foo']],
125+
['name' => ['$ne' => 'bar']],
126+
]],
127+
[], // options
128+
]],
129+
fn (Builder $builder) => $builder
130+
->whereNot('name', 'foo')
131+
->orWhere('name', '<>', 'bar'),
132+
];
133+
134+
/** @see DatabaseQueryBuilderTest::testWhereNot() */
135+
yield 'whereNot callable' => [
136+
['find' => [
137+
['$not' => ['name' => 'foo']],
138+
[], // options
139+
]],
140+
fn (Builder $builder) => $builder
141+
->whereNot(fn (Builder $q) => $q->where('name', 'foo')),
142+
];
143+
144+
yield 'where whereNot' => [
145+
['find' => [
146+
['$and' => [
147+
['name' => 'bar'],
148+
['$not' => ['email' => 'foo']],
149+
]],
150+
[], // options
151+
]],
152+
fn (Builder $builder) => $builder
153+
->where('name', '=', 'bar')
154+
->whereNot(function (Builder $q) {
155+
$q->where('email', '=', 'foo');
156+
}),
157+
];
158+
159+
yield 'whereNot (nested)' => [
160+
['find' => [
161+
['$not' => [
162+
'$and' => [
163+
['name' => 'foo'],
164+
['$not' => ['email' => ['$ne' => 'bar']]],
165+
],
166+
]],
167+
[], // options
168+
]],
169+
fn (Builder $builder) => $builder
170+
->whereNot(function (Builder $q) {
171+
$q->where('name', '=', 'foo')
172+
->whereNot('email', '<>', 'bar');
173+
}),
174+
];
175+
176+
yield 'orWhere orWhereNot' => [
177+
['find' => [
178+
['$or' => [
179+
['name' => 'bar'],
180+
['$not' => ['email' => 'foo']],
181+
]],
182+
[], // options
183+
]],
184+
fn (Builder $builder) => $builder
185+
->orWhere('name', '=', 'bar')
186+
->orWhereNot(function (Builder $q) {
187+
$q->where('email', '=', 'foo');
188+
}),
189+
];
190+
191+
yield 'where orWhereNot' => [
192+
['find' => [
193+
['$or' => [
194+
['name' => 'bar'],
195+
['$not' => ['email' => 'foo']],
196+
]],
197+
[], // options
198+
]],
199+
fn (Builder $builder) => $builder
200+
->where('name', '=', 'bar')
201+
->orWhereNot('email', '=', 'foo'),
202+
];
203+
204+
/** @see DatabaseQueryBuilderTest::testWhereNotWithArrayConditions() */
205+
yield 'whereNot with arrays of single condition' => [
206+
['find' => [
207+
['$not' => [
208+
'$and' => [
209+
['foo' => 1],
210+
['bar' => 2],
211+
],
212+
]],
213+
[], // options
214+
]],
215+
fn (Builder $builder) => $builder
216+
->whereNot([['foo', 1], ['bar', 2]]),
217+
];
218+
219+
yield 'whereNot with single array of conditions' => [
220+
['find' => [
221+
['$not' => [
222+
'$and' => [
223+
['foo' => 1],
224+
['bar' => 2],
225+
],
226+
]],
227+
[], // options
228+
]],
229+
fn (Builder $builder) => $builder
230+
->whereNot(['foo' => 1, 'bar' => 2]),
231+
];
232+
233+
yield 'whereNot with arrays of single condition with operator' => [
234+
['find' => [
235+
['$not' => [
236+
'$and' => [
237+
['foo' => 1],
238+
['bar' => ['$lt' => 2]],
239+
],
240+
]],
241+
[], // options
242+
]],
243+
fn (Builder $builder) => $builder
244+
->whereNot([
245+
['foo', 1],
246+
['bar', '<', 2],
247+
]),
248+
];
249+
68250
/** @see DatabaseQueryBuilderTest::testOrderBys() */
69251
yield 'orderBy multiple columns' => [
70252
['find' => [[], ['sort' => ['email' => 1, 'age' => -1]]]],

0 commit comments

Comments
 (0)