Skip to content

Commit f330412

Browse files
GromNaNDavpyu
andauthored
PHPORM-60 Fix Query on whereDate, whereDay, whereMonth, whereYear (#2572)
* Fix whereDate, whereMonth, whereYear, whereTime to use $expr and respective query rather than using basic comparison * Add and fix tests * Fix whereDate * PHPORM-60 Native support for whereTime * Remove magic extract to use explicit array access to options * Update whereDate * Support various time formats in whereTime --------- Co-authored-by: David <suryadavid@ymail.com>
1 parent af13eda commit f330412

File tree

5 files changed

+324
-48
lines changed

5 files changed

+324
-48
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
1717
- Fix validation of unique values when the validated value is found as part of an existing value. [#21](https://github.com/GromNaN/laravel-mongodb/pull/21) by [@GromNaN](https://github.com/GromNaN).
1818
- Support `%` and `_` in `like` expression [#17](https://github.com/GromNaN/laravel-mongodb/pull/17) by [@GromNaN](https://github.com/GromNaN).
1919
- Change signature of `Query\Builder::__constructor` to match the parent class [#26](https://github.com/GromNaN/laravel-mongodb-private/pull/26) by [@GromNaN](https://github.com/GromNaN).
20+
- Fix Query on `whereDate`, `whereDay`, `whereMonth`, `whereYear`, `whereTime` to use MongoDB operators [#2570](https://github.com/jenssegers/laravel-mongodb/pull/2376) by [@Davpyu](https://github.com/Davpyu) and [@GromNaN](https://github.com/GromNaN).
2021

2122
## [3.9.2] - 2022-09-01
2223

src/Query/Builder.php

+79-28
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Database\Query\Builder as BaseBuilder;
99
use Illuminate\Database\Query\Expression;
1010
use Illuminate\Support\Arr;
11+
use Illuminate\Support\Carbon;
1112
use Illuminate\Support\Collection;
1213
use Illuminate\Support\LazyCollection;
1314
use Jenssegers\Mongodb\Connection;
@@ -115,6 +116,7 @@ class Builder extends BaseBuilder
115116
* @var array
116117
*/
117118
protected $conversion = [
119+
'=' => 'eq',
118120
'!=' => 'ne',
119121
'<>' => 'ne',
120122
'<' => 'lt',
@@ -1075,7 +1077,7 @@ protected function compileWhereBasic(array $where): array
10751077
$operator = $operator === 'regex' ? '=' : 'not';
10761078
}
10771079

1078-
if (! isset($operator) || $operator == '=') {
1080+
if (! isset($operator) || $operator === '=' || $operator === 'eq') {
10791081
$query = [$column => $value];
10801082
} else {
10811083
$query = [$column => ['$'.$operator => $value]];
@@ -1180,12 +1182,35 @@ protected function compileWhereBetween(array $where): array
11801182
*/
11811183
protected function compileWhereDate(array $where): array
11821184
{
1183-
extract($where);
1184-
1185-
$where['operator'] = $operator;
1186-
$where['value'] = $value;
1185+
$startOfDay = new UTCDateTime(Carbon::parse($where['value'])->startOfDay());
1186+
$endOfDay = new UTCDateTime(Carbon::parse($where['value'])->endOfDay());
11871187

1188-
return $this->compileWhereBasic($where);
1188+
return match($where['operator']) {
1189+
'eq', '=' => [
1190+
$where['column'] => [
1191+
'$gte' => $startOfDay,
1192+
'$lte' => $endOfDay,
1193+
],
1194+
],
1195+
'ne' => [
1196+
$where['column'] => [
1197+
'$not' => [
1198+
'$gte' => $startOfDay,
1199+
'$lte' => $endOfDay,
1200+
],
1201+
],
1202+
],
1203+
'lt', 'gte' => [
1204+
$where['column'] => [
1205+
'$'.$where['operator'] => $startOfDay,
1206+
],
1207+
],
1208+
'gt', 'lte' => [
1209+
$where['column'] => [
1210+
'$'.$where['operator'] => $endOfDay,
1211+
],
1212+
],
1213+
};
11891214
}
11901215

11911216
/**
@@ -1194,12 +1219,16 @@ protected function compileWhereDate(array $where): array
11941219
*/
11951220
protected function compileWhereMonth(array $where): array
11961221
{
1197-
extract($where);
1198-
1199-
$where['operator'] = $operator;
1200-
$where['value'] = $value;
1201-
1202-
return $this->compileWhereBasic($where);
1222+
return [
1223+
'$expr' => [
1224+
'$'.$where['operator'] => [
1225+
[
1226+
'$month' => '$'.$where['column'],
1227+
],
1228+
(int) $where['value'],
1229+
],
1230+
],
1231+
];
12031232
}
12041233

12051234
/**
@@ -1208,12 +1237,16 @@ protected function compileWhereMonth(array $where): array
12081237
*/
12091238
protected function compileWhereDay(array $where): array
12101239
{
1211-
extract($where);
1212-
1213-
$where['operator'] = $operator;
1214-
$where['value'] = $value;
1215-
1216-
return $this->compileWhereBasic($where);
1240+
return [
1241+
'$expr' => [
1242+
'$'.$where['operator'] => [
1243+
[
1244+
'$dayOfMonth' => '$'.$where['column'],
1245+
],
1246+
(int) $where['value'],
1247+
],
1248+
],
1249+
];
12171250
}
12181251

12191252
/**
@@ -1222,12 +1255,16 @@ protected function compileWhereDay(array $where): array
12221255
*/
12231256
protected function compileWhereYear(array $where): array
12241257
{
1225-
extract($where);
1226-
1227-
$where['operator'] = $operator;
1228-
$where['value'] = $value;
1229-
1230-
return $this->compileWhereBasic($where);
1258+
return [
1259+
'$expr' => [
1260+
'$'.$where['operator'] => [
1261+
[
1262+
'$year' => '$'.$where['column'],
1263+
],
1264+
(int) $where['value'],
1265+
],
1266+
],
1267+
];
12311268
}
12321269

12331270
/**
@@ -1236,12 +1273,26 @@ protected function compileWhereYear(array $where): array
12361273
*/
12371274
protected function compileWhereTime(array $where): array
12381275
{
1239-
extract($where);
1276+
if (! is_string($where['value']) || ! preg_match('/^[0-2][0-9](:[0-6][0-9](:[0-6][0-9])?)?$/', $where['value'], $matches)) {
1277+
throw new \InvalidArgumentException(sprintf('Invalid time format, expected HH:MM:SS, HH:MM or HH, got "%s"', is_string($where['value']) ? $where['value'] : get_debug_type($where['value'])));
1278+
}
12401279

1241-
$where['operator'] = $operator;
1242-
$where['value'] = $value;
1280+
$format = match (count($matches)) {
1281+
1 => '%H',
1282+
2 => '%H:%M',
1283+
3 => '%H:%M:%S',
1284+
};
12431285

1244-
return $this->compileWhereBasic($where);
1286+
return [
1287+
'$expr' => [
1288+
'$'.$where['operator'] => [
1289+
[
1290+
'$dateToString' => ['date' => '$'.$where['column'], 'format' => $format],
1291+
],
1292+
$where['value'],
1293+
],
1294+
],
1295+
];
12451296
}
12461297

12471298
/**

tests/Models/Birthday.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
*
1212
* @property string $name
1313
* @property string $birthday
14-
* @property string $day
15-
* @property string $month
16-
* @property string $year
1714
* @property string $time
1815
*/
1916
class Birthday extends Eloquent
2017
{
2118
protected $connection = 'mongodb';
2219
protected $collection = 'birthday';
23-
protected $fillable = ['name', 'birthday', 'day', 'month', 'year', 'time'];
20+
protected $fillable = ['name', 'birthday'];
21+
22+
protected $casts = [
23+
'birthday' => 'datetime',
24+
];
2425
}

tests/Query/BuilderTest.php

+182
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,170 @@ function (Builder $builder) {
646646
fn (Builder $builder) => $builder->where('name', 'not regex', '/^acme$/si'),
647647
];
648648

649+
yield 'where date' => [
650+
['find' => [['created_at' => [
651+
'$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')),
652+
'$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')),
653+
]], []]],
654+
fn (Builder $builder) => $builder->whereDate('created_at', '2018-09-30'),
655+
];
656+
657+
yield 'where date DateTimeImmutable' => [
658+
['find' => [['created_at' => [
659+
'$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')),
660+
'$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')),
661+
]], []]],
662+
fn (Builder $builder) => $builder->whereDate('created_at', '=', new DateTimeImmutable('2018-09-30 15:00:00 +02:00')),
663+
];
664+
665+
yield 'where date !=' => [
666+
['find' => [['created_at' => [
667+
'$not' => [
668+
'$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')),
669+
'$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')),
670+
],
671+
]], []]],
672+
fn (Builder $builder) => $builder->whereDate('created_at', '!=', '2018-09-30'),
673+
];
674+
675+
yield 'where date <' => [
676+
['find' => [['created_at' => [
677+
'$lt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')),
678+
]], []]],
679+
fn (Builder $builder) => $builder->whereDate('created_at', '<', '2018-09-30'),
680+
];
681+
682+
yield 'where date >=' => [
683+
['find' => [['created_at' => [
684+
'$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')),
685+
]], []]],
686+
fn (Builder $builder) => $builder->whereDate('created_at', '>=', '2018-09-30'),
687+
];
688+
689+
yield 'where date >' => [
690+
['find' => [['created_at' => [
691+
'$gt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')),
692+
]], []]],
693+
fn (Builder $builder) => $builder->whereDate('created_at', '>', '2018-09-30'),
694+
];
695+
696+
yield 'where date <=' => [
697+
['find' => [['created_at' => [
698+
'$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')),
699+
]], []]],
700+
fn (Builder $builder) => $builder->whereDate('created_at', '<=', '2018-09-30'),
701+
];
702+
703+
yield 'where day' => [
704+
['find' => [['$expr' => [
705+
'$eq' => [
706+
['$dayOfMonth' => '$created_at'],
707+
5,
708+
],
709+
]], []]],
710+
fn (Builder $builder) => $builder->whereDay('created_at', 5),
711+
];
712+
713+
yield 'where day > string' => [
714+
['find' => [['$expr' => [
715+
'$gt' => [
716+
['$dayOfMonth' => '$created_at'],
717+
5,
718+
],
719+
]], []]],
720+
fn (Builder $builder) => $builder->whereDay('created_at', '>', '05'),
721+
];
722+
723+
yield 'where month' => [
724+
['find' => [['$expr' => [
725+
'$eq' => [
726+
['$month' => '$created_at'],
727+
10,
728+
],
729+
]], []]],
730+
fn (Builder $builder) => $builder->whereMonth('created_at', 10),
731+
];
732+
733+
yield 'where month > string' => [
734+
['find' => [['$expr' => [
735+
'$gt' => [
736+
['$month' => '$created_at'],
737+
5,
738+
],
739+
]], []]],
740+
fn (Builder $builder) => $builder->whereMonth('created_at', '>', '05'),
741+
];
742+
743+
yield 'where year' => [
744+
['find' => [['$expr' => [
745+
'$eq' => [
746+
['$year' => '$created_at'],
747+
2023,
748+
],
749+
]], []]],
750+
fn (Builder $builder) => $builder->whereYear('created_at', 2023),
751+
];
752+
753+
yield 'where year > string' => [
754+
['find' => [['$expr' => [
755+
'$gt' => [
756+
['$year' => '$created_at'],
757+
2023,
758+
],
759+
]], []]],
760+
fn (Builder $builder) => $builder->whereYear('created_at', '>', '2023'),
761+
];
762+
763+
yield 'where time HH:MM:SS' => [
764+
['find' => [['$expr' => [
765+
'$eq' => [
766+
['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']],
767+
'10:11:12',
768+
],
769+
]], []]],
770+
fn (Builder $builder) => $builder->whereTime('created_at', '10:11:12'),
771+
];
772+
773+
yield 'where time HH:MM' => [
774+
['find' => [['$expr' => [
775+
'$eq' => [
776+
['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M']],
777+
'10:11',
778+
],
779+
]], []]],
780+
fn (Builder $builder) => $builder->whereTime('created_at', '10:11'),
781+
];
782+
783+
yield 'where time HH' => [
784+
['find' => [['$expr' => [
785+
'$eq' => [
786+
['$dateToString' => ['date' => '$created_at', 'format' => '%H']],
787+
'10',
788+
],
789+
]], []]],
790+
fn (Builder $builder) => $builder->whereTime('created_at', '10'),
791+
];
792+
793+
yield 'where time DateTime' => [
794+
['find' => [['$expr' => [
795+
'$eq' => [
796+
['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']],
797+
'10:11:12',
798+
],
799+
]], []]],
800+
fn (Builder $builder) => $builder->whereTime('created_at', new \DateTimeImmutable('2023-08-22 10:11:12')),
801+
];
802+
803+
yield 'where time >' => [
804+
['find' => [['$expr' => [
805+
'$gt' => [
806+
['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']],
807+
'10:11:12',
808+
],
809+
]], []]],
810+
fn (Builder $builder) => $builder->whereTime('created_at', '>', '10:11:12'),
811+
];
812+
649813
/** @see DatabaseQueryBuilderTest::testBasicSelectDistinct */
650814
yield 'distinct' => [
651815
['distinct' => ['foo', [], []]],
@@ -774,6 +938,24 @@ public static function provideExceptions(): iterable
774938
'Missing expected ending delimiter "/" in regular expression "/foo#bar"',
775939
fn (Builder $builder) => $builder->where('name', 'regex', '/foo#bar'),
776940
];
941+
942+
yield 'whereTime with invalid time' => [
943+
\InvalidArgumentException::class,
944+
'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "10:11:12:13"',
945+
fn (Builder $builder) => $builder->whereTime('created_at', '10:11:12:13'),
946+
];
947+
948+
yield 'whereTime out of range' => [
949+
\InvalidArgumentException::class,
950+
'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "23:70"',
951+
fn (Builder $builder) => $builder->whereTime('created_at', '23:70'),
952+
];
953+
954+
yield 'whereTime invalid type' => [
955+
\InvalidArgumentException::class,
956+
'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "stdClass"',
957+
fn (Builder $builder) => $builder->whereTime('created_at', new \stdClass()),
958+
];
777959
}
778960

779961
/** @dataProvider getEloquentMethodsNotSupported */

0 commit comments

Comments
 (0)