From cc9dcca38bbed8145725782ff6bb371ed6e2d47f Mon Sep 17 00:00:00 2001 From: David Date: Sun, 27 Mar 2022 17:48:19 +0700 Subject: [PATCH 1/7] Fix whereDate, whereMonth, whereYear, whereTime to use $expr and respective query rather than using basic comparison --- CHANGELOG.md | 1 + src/Query/Builder.php | 86 +++++++++++++++++++++++++++++++++------ tests/Models/Birthday.php | 5 +-- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c10feb8..07d28c0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file. - 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). - Support `%` and `_` in `like` expression [#17](https://github.com/GromNaN/laravel-mongodb/pull/17) by [@GromNaN](https://github.com/GromNaN). - 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). +- Fix Query on `whereDate`, `whereDay`, `whereMonth`, `whereYear`, `whereTime` to use MongoDB operators [#2376](https://github.com/jenssegers/laravel-mongodb/pull/2376) by [@Davpyu](https://github.com/Davpyu) and [@GromNaN](https://github.com/GromNaN). ## [3.9.2] - 2022-09-01 diff --git a/src/Query/Builder.php b/src/Query/Builder.php index a65edd24a..9f60ee19e 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -8,6 +8,7 @@ use Illuminate\Database\Query\Builder as BaseBuilder; use Illuminate\Database\Query\Expression; use Illuminate\Support\Arr; +use Illuminate\Support\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\LazyCollection; use Jenssegers\Mongodb\Connection; @@ -1182,10 +1183,45 @@ protected function compileWhereDate(array $where): array { extract($where); - $where['operator'] = $operator; - $where['value'] = $value; + $startOfDay = new UTCDateTime(Carbon::parse($value)->startOfDay()); + $endOfDay = new UTCDateTime(Carbon::parse($value)->endOfDay()); - return $this->compileWhereBasic($where); + $operator = $this->conversion[$operator]; + + return match($operator) { + '=' => [ + $column => [ + '$gte' => $startOfDay, + '$lte' => $endOfDay, + ], + ], + '$ne' => [ + $column => [ + '$gt' => $endOfDay, + '$lt' => $startOfDay, + ], + ], + '$lt' => [ + $column => [ + '$lt' => $startOfDay, + ], + ], + '$gt' => [ + $column => [ + '$gt' => $endOfDay, + ], + ], + '$lte' => [ + $column => [ + '$lte' => $endOfDay, + ], + ], + '$gte' => [ + $column => [ + '$gte' => $startOfDay, + ], + ], + }; } /** @@ -1196,10 +1232,19 @@ protected function compileWhereMonth(array $where): array { extract($where); - $where['operator'] = $operator; - $where['value'] = $value; + $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; + $value = str_starts_with($value, '0') ? intval(str_replace('0', '', $value)) : $value; - return $this->compileWhereBasic($where); + return [ + '$expr' => [ + $operator => [ + [ + '$month' => '$'.$column + ], + $value, + ], + ], + ]; } /** @@ -1210,10 +1255,19 @@ protected function compileWhereDay(array $where): array { extract($where); - $where['operator'] = $operator; - $where['value'] = $value; + $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; + $value = str_starts_with($value, '0') ? intval(str_replace('0', '', $value)) : $value; - return $this->compileWhereBasic($where); + return [ + '$expr' => [ + $operator => [ + [ + '$dayOfMonth' => '$'.$column + ], + $value, + ], + ], + ]; } /** @@ -1224,10 +1278,18 @@ protected function compileWhereYear(array $where): array { extract($where); - $where['operator'] = $operator; - $where['value'] = $value; + $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; - return $this->compileWhereBasic($where); + return [ + '$expr' => [ + $operator => [ + [ + '$year' => '$'.$column + ], + $value + ], + ], + ]; } /** diff --git a/tests/Models/Birthday.php b/tests/Models/Birthday.php index 2afca41e0..df8c19ac3 100644 --- a/tests/Models/Birthday.php +++ b/tests/Models/Birthday.php @@ -11,14 +11,11 @@ * * @property string $name * @property string $birthday - * @property string $day - * @property string $month - * @property string $year * @property string $time */ class Birthday extends Eloquent { protected $connection = 'mongodb'; protected $collection = 'birthday'; - protected $fillable = ['name', 'birthday', 'day', 'month', 'year', 'time']; + protected $fillable = ['name', 'birthday', 'time']; } From 384e9d01fc732fb30b927a0c59710a9333e25f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 21 Aug 2023 18:01:23 +0200 Subject: [PATCH 2/7] Add and fix tests --- src/Query/Builder.php | 49 +++++++---------- tests/Models/Birthday.php | 4 ++ tests/Query/BuilderTest.php | 104 ++++++++++++++++++++++++++++++++++++ tests/QueryTest.php | 51 +++++++++++++----- 4 files changed, 164 insertions(+), 44 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 9f60ee19e..d09b1c7cd 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -116,6 +116,7 @@ class Builder extends BaseBuilder * @var array */ protected $conversion = [ + '=' => 'eq', '!=' => 'ne', '<>' => 'ne', '<' => 'lt', @@ -1076,7 +1077,7 @@ protected function compileWhereBasic(array $where): array $operator = $operator === 'regex' ? '=' : 'not'; } - if (! isset($operator) || $operator == '=') { + if (! isset($operator) || $operator === '=' || $operator === 'eq') { $query = [$column => $value]; } else { $query = [$column => ['$'.$operator => $value]]; @@ -1186,39 +1187,27 @@ protected function compileWhereDate(array $where): array $startOfDay = new UTCDateTime(Carbon::parse($value)->startOfDay()); $endOfDay = new UTCDateTime(Carbon::parse($value)->endOfDay()); - $operator = $this->conversion[$operator]; - return match($operator) { - '=' => [ + 'eq', '=' => [ $column => [ '$gte' => $startOfDay, '$lte' => $endOfDay, ], ], - '$ne' => [ + 'ne' => [ $column => [ '$gt' => $endOfDay, '$lt' => $startOfDay, ], ], - '$lt' => [ - $column => [ - '$lt' => $startOfDay, - ], - ], - '$gt' => [ - $column => [ - '$gt' => $endOfDay, - ], - ], - '$lte' => [ + 'lt', 'gte' => [ $column => [ - '$lte' => $endOfDay, + '$'.$operator => $startOfDay, ], ], - '$gte' => [ + 'gt', 'lte' => [ $column => [ - '$gte' => $startOfDay, + '$'.$operator => $endOfDay, ], ], }; @@ -1232,14 +1221,13 @@ protected function compileWhereMonth(array $where): array { extract($where); - $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; - $value = str_starts_with($value, '0') ? intval(str_replace('0', '', $value)) : $value; + $value = (int) ltrim($value, '0'); return [ '$expr' => [ - $operator => [ + '$'.$operator => [ [ - '$month' => '$'.$column + '$month' => '$'.$column, ], $value, ], @@ -1255,14 +1243,13 @@ protected function compileWhereDay(array $where): array { extract($where); - $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; - $value = str_starts_with($value, '0') ? intval(str_replace('0', '', $value)) : $value; + $value = (int) ltrim($value, '0'); return [ '$expr' => [ - $operator => [ + '$'.$operator => [ [ - '$dayOfMonth' => '$'.$column + '$dayOfMonth' => '$'.$column, ], $value, ], @@ -1278,15 +1265,15 @@ protected function compileWhereYear(array $where): array { extract($where); - $operator = $operator === '=' ? '$eq' : $this->conversion[$operator]; + $value = (int) $value; return [ '$expr' => [ - $operator => [ + '$'.$operator => [ [ - '$year' => '$'.$column + '$year' => '$'.$column, ], - $value + $value, ], ], ]; diff --git a/tests/Models/Birthday.php b/tests/Models/Birthday.php index df8c19ac3..3accb7a3b 100644 --- a/tests/Models/Birthday.php +++ b/tests/Models/Birthday.php @@ -18,4 +18,8 @@ class Birthday extends Eloquent protected $connection = 'mongodb'; protected $collection = 'birthday'; protected $fillable = ['name', 'birthday', 'time']; + + protected $casts = [ + 'birthday' => 'datetime', + ]; } diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 60c05e23f..1c5596c54 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -646,6 +646,110 @@ function (Builder $builder) { fn (Builder $builder) => $builder->where('name', 'not regex', '/^acme$/si'), ]; + yield 'where date' => [ + ['find' => [['created_at' => [ + '$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), + '$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '2018-09-30'), + ]; + + yield 'where date DateTimeImmutable' => [ + ['find' => [['created_at' => [ + '$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), + '$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '=', new DateTimeImmutable('2018-09-30 15:00:00 +02:00')), + ]; + + yield 'where date <' => [ + ['find' => [['created_at' => [ + '$lt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '<', '2018-09-30'), + ]; + + yield 'where date >=' => [ + ['find' => [['created_at' => [ + '$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '>=', '2018-09-30'), + ]; + + yield 'where date >' => [ + ['find' => [['created_at' => [ + '$gt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '>', '2018-09-30'), + ]; + + yield 'where date <=' => [ + ['find' => [['created_at' => [ + '$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')), + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '<=', '2018-09-30'), + ]; + + yield 'where day' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$dayOfMonth' => '$created_at'], + 5, + ], + ]], []]], + fn (Builder $builder) => $builder->whereDay('created_at', 5), + ]; + + yield 'where day > string' => [ + ['find' => [['$expr' => [ + '$gt' => [ + ['$dayOfMonth' => '$created_at'], + 5, + ], + ]], []]], + fn (Builder $builder) => $builder->whereDay('created_at', '>', '05'), + ]; + + yield 'where month' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$month' => '$created_at'], + 10, + ], + ]], []]], + fn (Builder $builder) => $builder->whereMonth('created_at', 10), + ]; + + yield 'where month > string' => [ + ['find' => [['$expr' => [ + '$gt' => [ + ['$month' => '$created_at'], + 5, + ], + ]], []]], + fn (Builder $builder) => $builder->whereMonth('created_at', '>', '05'), + ]; + + yield 'where year' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$year' => '$created_at'], + 2023, + ], + ]], []]], + fn (Builder $builder) => $builder->whereYear('created_at', 2023), + ]; + + yield 'where year > string' => [ + ['find' => [['$expr' => [ + '$gt' => [ + ['$year' => '$created_at'], + 2023, + ], + ]], []]], + fn (Builder $builder) => $builder->whereYear('created_at', '>', '2023'), + ]; + /** @see DatabaseQueryBuilderTest::testBasicSelectDistinct */ yield 'distinct' => [ ['distinct' => ['foo', [], []]], diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 754f204dc..9aca93523 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -4,6 +4,7 @@ namespace Jenssegers\Mongodb\Tests; +use DateTimeImmutable; use Jenssegers\Mongodb\Tests\Models\Birthday; use Jenssegers\Mongodb\Tests\Models\Scoped; use Jenssegers\Mongodb\Tests\Models\User; @@ -24,12 +25,12 @@ public function setUp(): void User::create(['name' => 'Tommy Toe', 'age' => 33, 'title' => 'user']); User::create(['name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin']); User::create(['name' => 'Error', 'age' => null, 'title' => null]); - Birthday::create(['name' => 'Mark Moe', 'birthday' => '2020-04-10', 'day' => '10', 'month' => '04', 'year' => '2020', 'time' => '10:53:11']); - Birthday::create(['name' => 'Jane Doe', 'birthday' => '2021-05-12', 'day' => '12', 'month' => '05', 'year' => '2021', 'time' => '10:53:12']); - Birthday::create(['name' => 'Harry Hoe', 'birthday' => '2021-05-11', 'day' => '11', 'month' => '05', 'year' => '2021', 'time' => '10:53:13']); - Birthday::create(['name' => 'Robert Doe', 'birthday' => '2021-05-12', 'day' => '12', 'month' => '05', 'year' => '2021', 'time' => '10:53:14']); - Birthday::create(['name' => 'Mark Moe', 'birthday' => '2021-05-12', 'day' => '12', 'month' => '05', 'year' => '2021', 'time' => '10:53:15']); - Birthday::create(['name' => 'Mark Moe', 'birthday' => '2022-05-12', 'day' => '12', 'month' => '05', 'year' => '2022', 'time' => '10:53:16']); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2020-04-10 10:53:11'), 'time' => '10:53:11']); + Birthday::create(['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:12'), 'time' => '10:53:12']); + Birthday::create(['name' => 'Harry Hoe', 'birthday' => new DateTimeImmutable('2021-05-11 10:53:13'), 'time' => '10:53:13']); + Birthday::create(['name' => 'Robert Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:14'), 'time' => '10:53:14']); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:15'), 'time' => '10:53:15']); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2022-05-12 10:53:16'), 'time' => '10:53:16']); } public function tearDown(): void @@ -204,36 +205,60 @@ public function testWhereDate(): void $birthdayCount = Birthday::whereDate('birthday', '2021-05-11')->get(); $this->assertCount(1, $birthdayCount); + + $birthdayCount = Birthday::whereDate('birthday', '>', '2021-05-11')->get(); + $this->assertCount(4, $birthdayCount); + + $birthdayCount = Birthday::whereDate('birthday', '>=', '2021-05-11')->get(); + $this->assertCount(5, $birthdayCount); + + $birthdayCount = Birthday::whereDate('birthday', '<', '2021-05-11')->get(); + $this->assertCount(1, $birthdayCount); + + $birthdayCount = Birthday::whereDate('birthday', '<=', '2021-05-11')->get(); + $this->assertCount(2, $birthdayCount); } public function testWhereDay(): void { - $day = Birthday::whereDay('day', '12')->get(); + $day = Birthday::whereDay('birthday', '12')->get(); $this->assertCount(4, $day); - $day = Birthday::whereDay('day', '11')->get(); + $day = Birthday::whereDay('birthday', '11')->get(); $this->assertCount(1, $day); } public function testWhereMonth(): void { - $month = Birthday::whereMonth('month', '04')->get(); + $month = Birthday::whereMonth('birthday', '04')->get(); $this->assertCount(1, $month); - $month = Birthday::whereMonth('month', '05')->get(); + $month = Birthday::whereMonth('birthday', '05')->get(); $this->assertCount(5, $month); + + $month = Birthday::whereMonth('birthday', '>=', '5')->get(); + $this->assertCount(5, $month); + + $month = Birthday::whereMonth('birthday', '<', '10')->get(); + $this->assertCount(6, $month); + + $month = Birthday::whereMonth('birthday', '<>', '5')->get(); + $this->assertCount(1, $month); } public function testWhereYear(): void { - $year = Birthday::whereYear('year', '2021')->get(); + $year = Birthday::whereYear('birthday', '2021')->get(); $this->assertCount(4, $year); - $year = Birthday::whereYear('year', '2022')->get(); + $year = Birthday::whereYear('birthday', '2022')->get(); $this->assertCount(1, $year); - $year = Birthday::whereYear('year', '<', '2021')->get(); + $year = Birthday::whereYear('birthday', '<', '2021')->get(); $this->assertCount(1, $year); + + $year = Birthday::whereYear('birthday', '<>', '2021')->get(); + $this->assertCount(2, $year); } public function testWhereTime(): void From 94aaaeb076cd745f92baaa9533f7394adc693b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 21 Aug 2023 18:20:04 +0200 Subject: [PATCH 3/7] Fix whereDate --- src/Query/Builder.php | 14 +++++++++++--- tests/Query/BuilderTest.php | 8 ++++++++ tests/QueryTest.php | 12 ++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index d09b1c7cd..f598904f9 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1195,9 +1195,17 @@ protected function compileWhereDate(array $where): array ], ], 'ne' => [ - $column => [ - '$gt' => $endOfDay, - '$lt' => $startOfDay, + '$or' => [ + [ + $column => [ + '$lt' => $startOfDay, + ], + ], + [ + $column => [ + '$gt' => $endOfDay, + ], + ], ], ], 'lt', 'gte' => [ diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 1c5596c54..c0d2a71a6 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -662,6 +662,14 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereDate('created_at', '=', new DateTimeImmutable('2018-09-30 15:00:00 +02:00')), ]; + yield 'where date !=' => [ + ['find' => [['$or' => [ + ['created_at' => ['$lt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00'))]], + ['created_at' => ['$gt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00'))]], + ]], []]], + fn (Builder $builder) => $builder->whereDate('created_at', '!=', '2018-09-30'), + ]; + yield 'where date <' => [ ['find' => [['created_at' => [ '$lt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 9aca93523..889f36da6 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -31,6 +31,7 @@ public function setUp(): void Birthday::create(['name' => 'Robert Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:14'), 'time' => '10:53:14']); Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:15'), 'time' => '10:53:15']); Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2022-05-12 10:53:16'), 'time' => '10:53:16']); + Birthday::create(['name' => 'Boo']); } public function tearDown(): void @@ -217,6 +218,9 @@ public function testWhereDate(): void $birthdayCount = Birthday::whereDate('birthday', '<=', '2021-05-11')->get(); $this->assertCount(2, $birthdayCount); + + $birthdayCount = Birthday::whereDate('birthday', '<>', '2021-05-11')->get(); + $this->assertCount(5, $birthdayCount); } public function testWhereDay(): void @@ -240,10 +244,10 @@ public function testWhereMonth(): void $this->assertCount(5, $month); $month = Birthday::whereMonth('birthday', '<', '10')->get(); - $this->assertCount(6, $month); + $this->assertCount(7, $month); $month = Birthday::whereMonth('birthday', '<>', '5')->get(); - $this->assertCount(1, $month); + $this->assertCount(2, $month); } public function testWhereYear(): void @@ -255,10 +259,10 @@ public function testWhereYear(): void $this->assertCount(1, $year); $year = Birthday::whereYear('birthday', '<', '2021')->get(); - $this->assertCount(1, $year); + $this->assertCount(2, $year); $year = Birthday::whereYear('birthday', '<>', '2021')->get(); - $this->assertCount(2, $year); + $this->assertCount(3, $year); } public function testWhereTime(): void From e95c0c8cebbe5b34fe5114e0b5ac574a4cec95d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 21 Aug 2023 21:51:13 +0200 Subject: [PATCH 4/7] PHPORM-60 Native support for whereTime --- CHANGELOG.md | 2 +- src/Query/Builder.php | 14 ++++++++++---- tests/Query/BuilderTest.php | 20 ++++++++++++++++++++ tests/QueryTest.php | 22 ++++++++++++++-------- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07d28c0c8..fc10adb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ All notable changes to this project will be documented in this file. - 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). - Support `%` and `_` in `like` expression [#17](https://github.com/GromNaN/laravel-mongodb/pull/17) by [@GromNaN](https://github.com/GromNaN). - 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). -- Fix Query on `whereDate`, `whereDay`, `whereMonth`, `whereYear`, `whereTime` to use MongoDB operators [#2376](https://github.com/jenssegers/laravel-mongodb/pull/2376) by [@Davpyu](https://github.com/Davpyu) and [@GromNaN](https://github.com/GromNaN). +- 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). ## [3.9.2] - 2022-09-01 diff --git a/src/Query/Builder.php b/src/Query/Builder.php index f598904f9..8fcd73926 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1295,10 +1295,16 @@ protected function compileWhereTime(array $where): array { extract($where); - $where['operator'] = $operator; - $where['value'] = $value; - - return $this->compileWhereBasic($where); + return [ + '$expr' => [ + '$'.$operator => [ + [ + '$dateToString' => ['date' => '$'.$column, 'format' => '%H:%M:%S'], + ], + $value, + ], + ], + ]; } /** diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index c0d2a71a6..7d8c8cd5f 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -758,6 +758,26 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereYear('created_at', '>', '2023'), ]; + yield 'where time' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']], + '10:11:12', + ], + ]], []]], + fn (Builder $builder) => $builder->whereTime('created_at', '10:11:12'), + ]; + + yield 'where time >' => [ + ['find' => [['$expr' => [ + '$gt' => [ + ['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']], + '10:11:12', + ], + ]], []]], + fn (Builder $builder) => $builder->whereTime('created_at', '>', '10:11:12'), + ]; + /** @see DatabaseQueryBuilderTest::testBasicSelectDistinct */ yield 'distinct' => [ ['distinct' => ['foo', [], []]], diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 889f36da6..0d3fef1c7 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -25,12 +25,12 @@ public function setUp(): void User::create(['name' => 'Tommy Toe', 'age' => 33, 'title' => 'user']); User::create(['name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin']); User::create(['name' => 'Error', 'age' => null, 'title' => null]); - Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2020-04-10 10:53:11'), 'time' => '10:53:11']); - Birthday::create(['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:12'), 'time' => '10:53:12']); - Birthday::create(['name' => 'Harry Hoe', 'birthday' => new DateTimeImmutable('2021-05-11 10:53:13'), 'time' => '10:53:13']); - Birthday::create(['name' => 'Robert Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:14'), 'time' => '10:53:14']); - Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:15'), 'time' => '10:53:15']); - Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2022-05-12 10:53:16'), 'time' => '10:53:16']); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2020-04-10 10:53:11')]); + Birthday::create(['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:12')]); + Birthday::create(['name' => 'Harry Hoe', 'birthday' => new DateTimeImmutable('2021-05-11 10:53:13')]); + Birthday::create(['name' => 'Robert Doe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:14')]); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2021-05-12 10:53:15')]); + Birthday::create(['name' => 'Mark Moe', 'birthday' => new DateTimeImmutable('2022-05-12 10:53:16')]); Birthday::create(['name' => 'Boo']); } @@ -267,11 +267,17 @@ public function testWhereYear(): void public function testWhereTime(): void { - $time = Birthday::whereTime('time', '10:53:11')->get(); + $time = Birthday::whereTime('birthday', '10:53:11')->get(); $this->assertCount(1, $time); - $time = Birthday::whereTime('time', '>=', '10:53:14')->get(); + $time = Birthday::whereTime('birthday', '>=', '10:53:14')->get(); $this->assertCount(3, $time); + + $time = Birthday::whereTime('birthday', '!=', '10:53:14')->get(); + $this->assertCount(6, $time); + + $time = Birthday::whereTime('birthday', '<', '10:53:12')->get(); + $this->assertCount(2, $time); } public function testOrder(): void From 7f198bd01f31995c1145f7a73be832a3de2a046a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 21 Aug 2023 23:41:44 +0200 Subject: [PATCH 5/7] Remove magic extract to use explicit array access to options --- src/Query/Builder.php | 60 ++++++++++++++------------------------- tests/Models/Birthday.php | 2 +- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 8fcd73926..e19331ad1 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1182,14 +1182,12 @@ protected function compileWhereBetween(array $where): array */ protected function compileWhereDate(array $where): array { - extract($where); - - $startOfDay = new UTCDateTime(Carbon::parse($value)->startOfDay()); - $endOfDay = new UTCDateTime(Carbon::parse($value)->endOfDay()); + $startOfDay = new UTCDateTime(Carbon::parse($where['value'])->startOfDay()); + $endOfDay = new UTCDateTime(Carbon::parse($where['value'])->endOfDay()); - return match($operator) { + return match($where['operator']) { 'eq', '=' => [ - $column => [ + $where['column'] => [ '$gte' => $startOfDay, '$lte' => $endOfDay, ], @@ -1197,25 +1195,25 @@ protected function compileWhereDate(array $where): array 'ne' => [ '$or' => [ [ - $column => [ + $where['column'] => [ '$lt' => $startOfDay, ], ], [ - $column => [ + $where['column'] => [ '$gt' => $endOfDay, ], ], ], ], 'lt', 'gte' => [ - $column => [ - '$'.$operator => $startOfDay, + $where['column'] => [ + '$'.$where['operator'] => $startOfDay, ], ], 'gt', 'lte' => [ - $column => [ - '$'.$operator => $endOfDay, + $where['column'] => [ + '$'.$where['operator'] => $endOfDay, ], ], }; @@ -1227,17 +1225,13 @@ protected function compileWhereDate(array $where): array */ protected function compileWhereMonth(array $where): array { - extract($where); - - $value = (int) ltrim($value, '0'); - return [ '$expr' => [ - '$'.$operator => [ + '$'.$where['operator'] => [ [ - '$month' => '$'.$column, + '$month' => '$'.$where['column'], ], - $value, + (int) $where['value'], ], ], ]; @@ -1249,17 +1243,13 @@ protected function compileWhereMonth(array $where): array */ protected function compileWhereDay(array $where): array { - extract($where); - - $value = (int) ltrim($value, '0'); - return [ '$expr' => [ - '$'.$operator => [ + '$'.$where['operator'] => [ [ - '$dayOfMonth' => '$'.$column, + '$dayOfMonth' => '$'.$where['column'], ], - $value, + (int) $where['value'], ], ], ]; @@ -1271,17 +1261,13 @@ protected function compileWhereDay(array $where): array */ protected function compileWhereYear(array $where): array { - extract($where); - - $value = (int) $value; - return [ '$expr' => [ - '$'.$operator => [ + '$'.$where['operator'] => [ [ - '$year' => '$'.$column, + '$year' => '$'.$where['column'], ], - $value, + (int) $where['value'], ], ], ]; @@ -1293,15 +1279,13 @@ protected function compileWhereYear(array $where): array */ protected function compileWhereTime(array $where): array { - extract($where); - return [ '$expr' => [ - '$'.$operator => [ + '$'.$where['operator'] => [ [ - '$dateToString' => ['date' => '$'.$column, 'format' => '%H:%M:%S'], + '$dateToString' => ['date' => '$'.$where['column'], 'format' => '%H:%M:%S'], ], - $value, + $where['value'], ], ], ]; diff --git a/tests/Models/Birthday.php b/tests/Models/Birthday.php index 3accb7a3b..712d18d3f 100644 --- a/tests/Models/Birthday.php +++ b/tests/Models/Birthday.php @@ -17,7 +17,7 @@ class Birthday extends Eloquent { protected $connection = 'mongodb'; protected $collection = 'birthday'; - protected $fillable = ['name', 'birthday', 'time']; + protected $fillable = ['name', 'birthday']; protected $casts = [ 'birthday' => 'datetime', From 5ce0d1f3b5dc8fe184ac913b327da8d0ae50c8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 22 Aug 2023 19:30:11 +0200 Subject: [PATCH 6/7] Update whereDate --- src/Query/Builder.php | 14 ++++---------- tests/Query/BuilderTest.php | 8 +++++--- tests/QueryTest.php | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index e19331ad1..5b6599c02 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1193,16 +1193,10 @@ protected function compileWhereDate(array $where): array ], ], 'ne' => [ - '$or' => [ - [ - $where['column'] => [ - '$lt' => $startOfDay, - ], - ], - [ - $where['column'] => [ - '$gt' => $endOfDay, - ], + $where['column'] => [ + '$not' => [ + '$gte' => $startOfDay, + '$lte' => $endOfDay, ], ], ], diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 7d8c8cd5f..79e597de8 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -663,9 +663,11 @@ function (Builder $builder) { ]; yield 'where date !=' => [ - ['find' => [['$or' => [ - ['created_at' => ['$lt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00'))]], - ['created_at' => ['$gt' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00'))]], + ['find' => [['created_at' => [ + '$not' => [ + '$gte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 00:00:00.000 +00:00')), + '$lte' => new UTCDateTime(new DateTimeImmutable('2018-09-30 23:59:59.999 +00:00')), + ], ]], []]], fn (Builder $builder) => $builder->whereDate('created_at', '!=', '2018-09-30'), ]; diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 0d3fef1c7..52ff6f850 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -220,7 +220,7 @@ public function testWhereDate(): void $this->assertCount(2, $birthdayCount); $birthdayCount = Birthday::whereDate('birthday', '<>', '2021-05-11')->get(); - $this->assertCount(5, $birthdayCount); + $this->assertCount(6, $birthdayCount); } public function testWhereDay(): void From 0c525c40379e02770e5c7798d81128c17de56619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 22 Aug 2023 20:08:51 +0200 Subject: [PATCH 7/7] Support various time formats in whereTime --- src/Query/Builder.php | 12 ++++++++- tests/Query/BuilderTest.php | 50 ++++++++++++++++++++++++++++++++++++- tests/QueryTest.php | 6 +++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 5b6599c02..682c70c19 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1273,11 +1273,21 @@ protected function compileWhereYear(array $where): array */ protected function compileWhereTime(array $where): array { + if (! is_string($where['value']) || ! preg_match('/^[0-2][0-9](:[0-6][0-9](:[0-6][0-9])?)?$/', $where['value'], $matches)) { + 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']))); + } + + $format = match (count($matches)) { + 1 => '%H', + 2 => '%H:%M', + 3 => '%H:%M:%S', + }; + return [ '$expr' => [ '$'.$where['operator'] => [ [ - '$dateToString' => ['date' => '$'.$where['column'], 'format' => '%H:%M:%S'], + '$dateToString' => ['date' => '$'.$where['column'], 'format' => $format], ], $where['value'], ], diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 79e597de8..8e76840af 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -760,7 +760,7 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereYear('created_at', '>', '2023'), ]; - yield 'where time' => [ + yield 'where time HH:MM:SS' => [ ['find' => [['$expr' => [ '$eq' => [ ['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']], @@ -770,6 +770,36 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereTime('created_at', '10:11:12'), ]; + yield 'where time HH:MM' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M']], + '10:11', + ], + ]], []]], + fn (Builder $builder) => $builder->whereTime('created_at', '10:11'), + ]; + + yield 'where time HH' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$dateToString' => ['date' => '$created_at', 'format' => '%H']], + '10', + ], + ]], []]], + fn (Builder $builder) => $builder->whereTime('created_at', '10'), + ]; + + yield 'where time DateTime' => [ + ['find' => [['$expr' => [ + '$eq' => [ + ['$dateToString' => ['date' => '$created_at', 'format' => '%H:%M:%S']], + '10:11:12', + ], + ]], []]], + fn (Builder $builder) => $builder->whereTime('created_at', new \DateTimeImmutable('2023-08-22 10:11:12')), + ]; + yield 'where time >' => [ ['find' => [['$expr' => [ '$gt' => [ @@ -908,6 +938,24 @@ public static function provideExceptions(): iterable 'Missing expected ending delimiter "/" in regular expression "/foo#bar"', fn (Builder $builder) => $builder->where('name', 'regex', '/foo#bar'), ]; + + yield 'whereTime with invalid time' => [ + \InvalidArgumentException::class, + 'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "10:11:12:13"', + fn (Builder $builder) => $builder->whereTime('created_at', '10:11:12:13'), + ]; + + yield 'whereTime out of range' => [ + \InvalidArgumentException::class, + 'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "23:70"', + fn (Builder $builder) => $builder->whereTime('created_at', '23:70'), + ]; + + yield 'whereTime invalid type' => [ + \InvalidArgumentException::class, + 'Invalid time format, expected HH:MM:SS, HH:MM or HH, got "stdClass"', + fn (Builder $builder) => $builder->whereTime('created_at', new \stdClass()), + ]; } /** @dataProvider getEloquentMethodsNotSupported */ diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 52ff6f850..8737a7d68 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -270,6 +270,12 @@ public function testWhereTime(): void $time = Birthday::whereTime('birthday', '10:53:11')->get(); $this->assertCount(1, $time); + $time = Birthday::whereTime('birthday', '10:53')->get(); + $this->assertCount(6, $time); + + $time = Birthday::whereTime('birthday', '10')->get(); + $this->assertCount(6, $time); + $time = Birthday::whereTime('birthday', '>=', '10:53:14')->get(); $this->assertCount(3, $time);