Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.

Commit c612c24

Browse files
committed
PHPORM-47 Improve Builder::whereBetween to support CarbonPeriod and reject invalid array
1 parent 8562a4b commit c612c24

File tree

2 files changed

+222
-5
lines changed

2 files changed

+222
-5
lines changed

src/Query/Builder.php

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Jenssegers\Mongodb\Query;
44

5+
use Carbon\CarbonPeriod;
56
use Closure;
67
use DateTimeInterface;
78
use Illuminate\Database\Query\Builder as BaseBuilder;
@@ -554,11 +555,25 @@ public function whereAll($column, array $values, $boolean = 'and', $not = false)
554555

555556
/**
556557
* @inheritdoc
558+
* @param array{mixed, mixed}|CarbonPeriod $values
557559
*/
558560
public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
559561
{
560562
$type = 'between';
561563

564+
if ($values instanceof Collection) {
565+
$values = $values->all();
566+
}
567+
568+
if (is_array($values)) {
569+
if (!array_is_list($values)) {
570+
throw new \InvalidArgumentException('Between array must a list with 2 elements: [min, max]');
571+
}
572+
if (count($values) !== 2) {
573+
throw new \InvalidArgumentException('Between array must have 2 elements: [min, max]');
574+
}
575+
}
576+
562577
$this->wheres[] = compact('column', 'type', 'boolean', 'values', 'not');
563578

564579
return $this;
@@ -995,11 +1010,18 @@ protected function compileWheres(): array
9951010
}
9961011
}
9971012
} elseif (isset($where['values'])) {
998-
array_walk_recursive($where['values'], function (&$item, $key) {
999-
if ($item instanceof DateTimeInterface) {
1000-
$item = new UTCDateTime($item);
1001-
}
1002-
});
1013+
if (is_array($where['values'])) {
1014+
array_walk_recursive($where['values'], function (&$item, $key) {
1015+
if ($item instanceof DateTimeInterface) {
1016+
$item = new UTCDateTime($item);
1017+
}
1018+
});
1019+
} elseif ($where['values'] instanceof CarbonPeriod) {
1020+
$where['values'] = [
1021+
new UTCDateTime($where['values']->getStartDate()),
1022+
new UTCDateTime($where['values']->getEndDate()),
1023+
];
1024+
}
10031025
}
10041026

10051027
// The next item in a "chain" of wheres devices the boolean of the

tests/Query/BuilderTest.php

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Jenssegers\Mongodb\Tests\Query;
66

77
use DateTimeImmutable;
8+
use Illuminate\Database\Eloquent\Collection;
89
use Illuminate\Tests\Database\DatabaseQueryBuilderTest;
910
use Jenssegers\Mongodb\Connection;
1011
use Jenssegers\Mongodb\Query\Builder;
@@ -124,6 +125,174 @@ function (Builder $builder) {
124125
->orderBy('score', ['$meta' => 'textScore']),
125126
];
126127

128+
/** @see DatabaseQueryBuilderTest::testWhereBetweens() */
129+
yield 'whereBetween array of numbers' => [
130+
['find' => [
131+
['id' => ['$gte' => 1, '$lte' => 2]],
132+
['typeMap' => ['root' => 'array', 'document' => 'array']]],
133+
],
134+
fn (Builder $builder) => $builder->whereBetween('id', [1, 2]),
135+
];
136+
137+
/*
138+
yield 'whereBetween excessive nested array of numbers' => [
139+
['find' => [
140+
['id' => ['$gte' => 1, '$lte' => 2]],
141+
['typeMap' => ['root' => 'array', 'document' => 'array']]],
142+
],
143+
fn (Builder $builder) => $builder->whereBetween('id', [[1, 2, 3]]),
144+
];
145+
146+
yield 'whereBetween nested array of numbers' => [
147+
['find' => [
148+
['id' => ['$gte' => 1, '$lte' => 2]],
149+
['typeMap' => ['root' => 'array', 'document' => 'array']]],
150+
],
151+
fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]),
152+
];
153+
*/
154+
155+
yield 'whereNotBetween array of numbers' => [
156+
['find' => [
157+
['$or' => [['id' => ['$lte' => 1]], ['id' => ['$gte' => 2]]]],
158+
['typeMap' => ['root' => 'array', 'document' => 'array']]],
159+
],
160+
fn (Builder $builder) => $builder->whereNotBetween('id', [1, 2]),
161+
];
162+
163+
$period = now()->toPeriod(now()->addDay());
164+
yield 'whereBetween CarbonPeriod' => [
165+
['find' => [
166+
['created_at' => ['$gte' => new UTCDateTime($period->start), '$lte' => new UTCDateTime($period->end)]],
167+
[]
168+
]],
169+
fn (Builder $builder) => $builder->whereBetween('created_at', $period),
170+
];
171+
172+
$period = now()->toPeriod(now()->addMonth());
173+
yield 'custom long carbon period date' => [
174+
['find' => [
175+
['created_at' => ['$gte' => new UTCDateTime($period->start), '$lte' => new UTCDateTime($period->end)]],
176+
[]
177+
]],
178+
fn (Builder $builder) => $builder->whereBetween('created_at', $period),
179+
];
180+
181+
yield 'whereBetween collection' => [
182+
['find' => [
183+
['id' => ['$gte' => 1, '$lte' => 2]],
184+
[]
185+
]],
186+
fn (Builder $builder) => $builder->whereBetween('id', collect([1, 2])),
187+
];
188+
189+
/** @see DatabaseQueryBuilderTest::testOrWhereBetween() */
190+
yield 'whereBetween array numbers' => [
191+
['find' => [
192+
['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 5]]]],
193+
[]
194+
]],
195+
fn (Builder $builder) => $builder
196+
->where('id', '=', 1)
197+
->orWhereBetween('id', [3, 5]),
198+
];
199+
200+
/*
201+
yield 'whereBetween excessive nested array numbers' => [
202+
['find' => [
203+
['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 4]]]],
204+
[]
205+
]],
206+
fn (Builder $builder) => $builder
207+
->where('id', '=', 1)
208+
->orWhereBetween('id', [[3, 4, 5]]),
209+
];
210+
211+
yield 'orWhereBetween nested array numbers' => [
212+
['find' => [
213+
['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 5]]]],
214+
[]
215+
]],
216+
fn (Builder $builder) => $builder
217+
->where('id', '=', 1)
218+
->orWhereBetween('id', [[3, 5]]),
219+
];
220+
221+
yield 'orWhereBetween nested excessive array numbers' => [
222+
['find' => [
223+
['$or' => [['id' => 1], ['id' => ['$gte' => 4, '$lte' => 6]]]],
224+
[]
225+
]],
226+
fn (Builder $builder) => $builder
227+
->where('id', '=', 1)
228+
->orWhereBetween('id', [[4], [6, 8]]),
229+
];
230+
*/
231+
232+
yield 'orWhereBetween collection' => [
233+
['find' => [
234+
['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 4]]]],
235+
[]
236+
]],
237+
fn (Builder $builder) => $builder
238+
->where('id', '=', 1)
239+
->orWhereBetween('id', collect([3, 4])),
240+
];
241+
242+
/** @see DatabaseQueryBuilderTest::testOrWhereNotBetween() */
243+
yield 'orWhereNotBetween array of numbers' => [
244+
['find' => [
245+
['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 5]]]]]],
246+
[]
247+
]],
248+
fn (Builder $builder) => $builder
249+
->where('id', '=', 1)
250+
->orWhereNotBetween('id', [3, 5]),
251+
];
252+
253+
/*
254+
yield 'orWhereNotBetween excessive array of numbers' => [
255+
['find' => [
256+
['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 4]]]]]],
257+
[]
258+
]],
259+
fn (Builder $builder) => $builder
260+
->where('id', '=', 1)
261+
->orWhereNotBetween('id', [[3, 4, 5]]),
262+
];
263+
264+
yield 'orWhereNotBetween nested array of numbers' => [
265+
['find' => [
266+
['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 5]]]]]],
267+
[]
268+
]],
269+
fn (Builder $builder) => $builder
270+
->where('id', '=', 1)
271+
->orWhereNotBetween('id', [[3, 5]]),
272+
];
273+
*/
274+
275+
yield 'orWhereNotBetween excessive nested array of numbers' => [
276+
['find' => [
277+
['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => [4]]], ['id' => ['$gte' => [6, 8]]]]]]],
278+
//['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 4]], ['id' => ['$gte' => 6]]]]]],
279+
[]
280+
]],
281+
fn (Builder $builder) => $builder
282+
->where('id', '=', 1)
283+
->orWhereNotBetween('id', [[4], [6, 8]]),
284+
];
285+
286+
yield 'orWhereNotBetween collection' => [
287+
['find' => [
288+
['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 4]]]]]],
289+
[]
290+
]],
291+
fn (Builder $builder) => $builder
292+
->where('id', '=', 1)
293+
->orWhereNotBetween('id', collect([3, 4])),
294+
];
295+
127296
yield 'distinct' => [
128297
['distinct' => ['foo', [], []]],
129298
fn (Builder $builder) => $builder->distinct('foo'),
@@ -154,6 +323,32 @@ public static function provideExceptions(): iterable
154323
'Order direction must be "asc" or "desc"',
155324
fn (Builder $builder) => $builder->orderBy('_id', 'dasc'),
156325
];
326+
327+
/** @see DatabaseQueryBuilderTest::testWhereBetweens */
328+
yield 'whereBetween array too short' => [
329+
\InvalidArgumentException::class,
330+
'Between array must have 2 elements: [min, max]',
331+
fn (Builder $builder) => $builder->whereBetween('id', [1]),
332+
];
333+
334+
yield 'whereBetween array too long' => [
335+
\InvalidArgumentException::class,
336+
'Between array must have 2 elements: [min, max]',
337+
fn (Builder $builder) => $builder->whereBetween('id', [1, 2, 3]),
338+
];
339+
340+
yield 'whereBetween collection too long' => [
341+
\InvalidArgumentException::class,
342+
'Between array must have 2 elements: [min, max]',
343+
fn (Builder $builder) => $builder->whereBetween('id', new Collection([1, 2, 3])),
344+
];
345+
346+
yield 'whereBetween array is not a list' => [
347+
\InvalidArgumentException::class,
348+
'Between array must a list with 2 elements: [min, max]',
349+
fn (Builder $builder) => $builder->whereBetween('id', ['min' => 1, 'max' => 2]),
350+
];
351+
157352
}
158353

159354
private static function getBuilder(): Builder

0 commit comments

Comments
 (0)