diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 6168159df..fef4eb45c 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -32,7 +32,6 @@ use function array_map; use function array_merge; use function array_values; -use function array_walk_recursive; use function assert; use function blank; use function call_user_func; @@ -689,17 +688,7 @@ public function insert(array $values) $values = [$values]; } - // Compatibility with Eloquent queries that uses "id" instead of MongoDB's _id - foreach ($values as &$document) { - if (isset($document['id'])) { - if (isset($document['_id']) && $document['_id'] !== $document['id']) { - throw new InvalidArgumentException('Cannot insert document with different "id" and "_id" values'); - } - - $document['_id'] = $document['id']; - unset($document['id']); - } - } + $values = $this->aliasIdForQuery($values); $options = $this->inheritConnectionOptions(); @@ -876,6 +865,7 @@ public function delete($id = null) } $wheres = $this->compileWheres(); + $wheres = $this->aliasIdForQuery($wheres); $options = $this->inheritConnectionOptions(); if (is_int($this->limit)) { @@ -1070,16 +1060,12 @@ protected function performUpdate(array $update, array $options = []) $options['multiple'] = true; } - // Since "id" is an alias for "_id", we prevent updating it - foreach ($update as $operator => $fields) { - if (array_key_exists('id', $fields)) { - throw new InvalidArgumentException('Cannot update "id" field.'); - } - } + $update = $this->aliasIdForQuery($update); $options = $this->inheritConnectionOptions($options); $wheres = $this->compileWheres(); + $wheres = $this->aliasIdForQuery($wheres); $result = $this->collection->updateMany($wheres, $update, $options); if ($result->isAcknowledged()) { return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount(); @@ -1191,32 +1177,12 @@ protected function compileWheres(): array } } - // Convert DateTime values to UTCDateTime. - if (isset($where['value'])) { - if (is_array($where['value'])) { - array_walk_recursive($where['value'], function (&$item, $key) { - if ($item instanceof DateTimeInterface) { - $item = new UTCDateTime($item); - } - }); - } else { - if ($where['value'] instanceof DateTimeInterface) { - $where['value'] = new UTCDateTime($where['value']); - } - } - } elseif (isset($where['values'])) { - if (is_array($where['values'])) { - array_walk_recursive($where['values'], function (&$item, $key) { - if ($item instanceof DateTimeInterface) { - $item = new UTCDateTime($item); - } - }); - } elseif ($where['values'] instanceof CarbonPeriod) { - $where['values'] = [ - new UTCDateTime($where['values']->getStartDate()), - new UTCDateTime($where['values']->getEndDate()), - ]; - } + // Convert CarbonPeriod to DateTime interval. + if (isset($where['values']) && $where['values'] instanceof CarbonPeriod) { + $where['values'] = [ + $where['values']->getStartDate(), + $where['values']->getEndDate(), + ]; } // In a sequence of "where" clauses, the logical operator of the @@ -1631,12 +1597,21 @@ public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and') private function aliasIdForQuery(array $values): array { if (array_key_exists('id', $values)) { + if (array_key_exists('_id', $values)) { + throw new InvalidArgumentException('Cannot have both "id" and "_id" fields.'); + } + $values['_id'] = $values['id']; unset($values['id']); } foreach ($values as $key => $value) { if (is_string($key) && str_ends_with($key, '.id')) { + $newkey = substr($key, 0, -3) . '._id'; + if (array_key_exists($newkey, $values)) { + throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newkey)); + } + $values[substr($key, 0, -3) . '._id'] = $value; unset($values[$key]); } @@ -1645,6 +1620,8 @@ private function aliasIdForQuery(array $values): array foreach ($values as &$value) { if (is_array($value)) { $value = $this->aliasIdForQuery($value); + } elseif ($value instanceof DateTimeInterface) { + $value = new UTCDateTime($value); } } diff --git a/tests/Query/AggregationBuilderTest.php b/tests/Query/AggregationBuilderTest.php index b3828597d..a355db439 100644 --- a/tests/Query/AggregationBuilderTest.php +++ b/tests/Query/AggregationBuilderTest.php @@ -11,7 +11,6 @@ use InvalidArgumentException; use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; -use MongoDB\BSON\UTCDateTime; use MongoDB\Builder\BuilderEncoder; use MongoDB\Builder\Expression; use MongoDB\Builder\Pipeline; @@ -33,8 +32,8 @@ public function tearDown(): void public function testCreateAggregationBuilder(): void { User::insert([ - ['name' => 'John Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1989-01-01'))], - ['name' => 'Jane Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1990-01-01'))], + ['name' => 'John Doe', 'birthday' => new DateTimeImmutable('1989-01-01')], + ['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('1990-01-01')], ]); // Create the aggregation pipeline from the query builder diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index b081a0557..666747a46 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -566,6 +566,12 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]), ]; + $date = new DateTimeImmutable('2018-09-30 15:00:00 +02:00'); + yield 'where $lt DateTimeInterface' => [ + ['find' => [['created_at' => ['$lt' => new UTCDateTime($date)]], []]], + fn (Builder $builder) => $builder->where('created_at', '<', $date), + ]; + $period = now()->toPeriod(now()->addMonth()); yield 'whereBetween CarbonPeriod' => [ [ diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 6b08a15b7..0495f38aa 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -1053,16 +1053,20 @@ public function testIncrementEach() #[TestWith(['id', 'id'])] #[TestWith(['id', '_id'])] #[TestWith(['_id', 'id'])] + #[TestWith(['_id', '_id'])] public function testIdAlias($insertId, $queryId): void { - DB::collection('items')->insert([$insertId => 'abc', 'name' => 'Karting']); - $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + DB::table('items')->insert([$insertId => 'abc', 'name' => 'Karting']); + $item = DB::table('items')->where($queryId, '=', 'abc')->first(); $this->assertNotNull($item); $this->assertSame('abc', $item['id']); $this->assertSame('Karting', $item['name']); - DB::collection('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); - $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + DB::table('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); + $item = DB::table('items')->where($queryId, '=', 'abc')->first(); $this->assertSame('Bike', $item['name']); + + $result = DB::table('items')->where($queryId, '=', 'abc')->delete(); + $this->assertSame(1, $result); } }