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

Commit 4dfab8c

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

File tree

2 files changed

+170
-5
lines changed

2 files changed

+170
-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: 143 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,122 @@ function (Builder $builder) {
124125
->orderBy('score', ['$meta' => 'textScore']),
125126
];
126127

128+
/** @see DatabaseQueryBuilderTest::testWhereBetweens() */
129+
yield 'whereBetween array of numbers' => [
130+
['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []]],
131+
fn (Builder $builder) => $builder->whereBetween('id', [1, 2]),
132+
];
133+
134+
/*
135+
yield 'whereBetween excessive nested array of numbers' => [
136+
['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []],
137+
fn (Builder $builder) => $builder->whereBetween('id', [[1, 2, 3]]),
138+
];
139+
140+
yield 'whereBetween nested array of numbers' => [
141+
['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []]],
142+
fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]),
143+
];
144+
*/
145+
146+
yield 'whereNotBetween array of numbers' => [
147+
['find' => [['$or' => [['id' => ['$lte' => 1]], ['id' => ['$gte' => 2]]]], []]],
148+
fn (Builder $builder) => $builder->whereNotBetween('id', [1, 2]),
149+
];
150+
151+
$period = now()->toPeriod(now()->addDay());
152+
yield 'whereBetween CarbonPeriod' => [
153+
['find' => [['created_at' => ['$gte' => new UTCDateTime($period->start), '$lte' => new UTCDateTime($period->end)]], []]],
154+
fn (Builder $builder) => $builder->whereBetween('created_at', $period),
155+
];
156+
157+
$period = now()->toPeriod(now()->addMonth());
158+
yield 'custom long carbon period date' => [
159+
['find' => [['created_at' => ['$gte' => new UTCDateTime($period->start), '$lte' => new UTCDateTime($period->end)]], []]],
160+
fn (Builder $builder) => $builder->whereBetween('created_at', $period),
161+
];
162+
163+
yield 'whereBetween collection' => [
164+
['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []]],
165+
fn (Builder $builder) => $builder->whereBetween('id', collect([1, 2])),
166+
];
167+
168+
/** @see DatabaseQueryBuilderTest::testOrWhereBetween() */
169+
yield 'whereBetween array numbers' => [
170+
['find' => [['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 5]]]], []]],
171+
fn (Builder $builder) => $builder
172+
->where('id', '=', 1)
173+
->orWhereBetween('id', [3, 5]),
174+
];
175+
176+
/*
177+
yield 'whereBetween excessive nested array numbers' => [
178+
['find' => [['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 4]]]], []],
179+
fn (Builder $builder) => $builder
180+
->where('id', '=', 1)
181+
->orWhereBetween('id', [[3, 4, 5]]),
182+
];
183+
184+
yield 'orWhereBetween nested array numbers' => [
185+
['find' => [['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 5]]]], []],
186+
fn (Builder $builder) => $builder
187+
->where('id', '=', 1)
188+
->orWhereBetween('id', [[3, 5]]),
189+
];
190+
191+
yield 'orWhereBetween nested excessive array numbers' => [
192+
['find' => [['$or' => [['id' => 1], ['id' => ['$gte' => 4, '$lte' => 6]]]], []],
193+
fn (Builder $builder) => $builder
194+
->where('id', '=', 1)
195+
->orWhereBetween('id', [[4], [6, 8]]),
196+
];
197+
*/
198+
199+
yield 'orWhereBetween collection' => [
200+
['find' => [['$or' => [['id' => 1], ['id' => ['$gte' => 3, '$lte' => 4]]]], []]],
201+
fn (Builder $builder) => $builder
202+
->where('id', '=', 1)
203+
->orWhereBetween('id', collect([3, 4])),
204+
];
205+
206+
/** @see DatabaseQueryBuilderTest::testOrWhereNotBetween() */
207+
yield 'orWhereNotBetween array of numbers' => [
208+
['find' => [['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 5]]]]]], []]],
209+
fn (Builder $builder) => $builder
210+
->where('id', '=', 1)
211+
->orWhereNotBetween('id', [3, 5]),
212+
];
213+
214+
/*
215+
yield 'orWhereNotBetween excessive array of numbers' => [
216+
['find' => [['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 4]]]]]], []],
217+
fn (Builder $builder) => $builder
218+
->where('id', '=', 1)
219+
->orWhereNotBetween('id', [[3, 4, 5]]),
220+
];
221+
222+
yield 'orWhereNotBetween nested array of numbers' => [
223+
['find' => [['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 5]]]]]], []],
224+
fn (Builder $builder) => $builder
225+
->where('id', '=', 1)
226+
->orWhereNotBetween('id', [[3, 5]]),
227+
];
228+
*/
229+
230+
yield 'orWhereNotBetween excessive nested array of numbers' => [
231+
['find' => [['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => [4]]], ['id' => ['$gte' => [6, 8]]]]]]]]],
232+
fn (Builder $builder) => $builder
233+
->where('id', '=', 1)
234+
->orWhereNotBetween('id', [[4], [6, 8]]),
235+
];
236+
237+
yield 'orWhereNotBetween collection' => [
238+
['find' => [['$or' => [['id' => 1], ['$or' => [['id' => ['$lte' => 3]], ['id' => ['$gte' => 4]]]]]], []]],
239+
fn (Builder $builder) => $builder
240+
->where('id', '=', 1)
241+
->orWhereNotBetween('id', collect([3, 4])),
242+
];
243+
127244
yield 'distinct' => [
128245
['distinct' => ['foo', [], []]],
129246
fn (Builder $builder) => $builder->distinct('foo'),
@@ -154,6 +271,32 @@ public static function provideExceptions(): iterable
154271
'Order direction must be "asc" or "desc"',
155272
fn (Builder $builder) => $builder->orderBy('_id', 'dasc'),
156273
];
274+
275+
/** @see DatabaseQueryBuilderTest::testWhereBetweens */
276+
yield 'whereBetween array too short' => [
277+
\InvalidArgumentException::class,
278+
'Between array must have 2 elements: [min, max]',
279+
fn (Builder $builder) => $builder->whereBetween('id', [1]),
280+
];
281+
282+
yield 'whereBetween array too long' => [
283+
\InvalidArgumentException::class,
284+
'Between array must have 2 elements: [min, max]',
285+
fn (Builder $builder) => $builder->whereBetween('id', [1, 2, 3]),
286+
];
287+
288+
yield 'whereBetween collection too long' => [
289+
\InvalidArgumentException::class,
290+
'Between array must have 2 elements: [min, max]',
291+
fn (Builder $builder) => $builder->whereBetween('id', new Collection([1, 2, 3])),
292+
];
293+
294+
yield 'whereBetween array is not a list' => [
295+
\InvalidArgumentException::class,
296+
'Between array must a list with 2 elements: [min, max]',
297+
fn (Builder $builder) => $builder->whereBetween('id', ['min' => 1, 'max' => 2]),
298+
];
299+
157300
}
158301

159302
private static function getBuilder(): Builder

0 commit comments

Comments
 (0)