From a3332a764c90955406dcf9762a63d23816a6d3de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 22 May 2024 17:33:08 +0200 Subject: [PATCH 1/3] Unset _id null to let it be autogenerated --- src/Eloquent/Builder.php | 9 +++++---- src/Eloquent/Model.php | 6 ++++++ tests/ModelTest.php | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index 22dcfd081..10cb94e67 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -21,6 +21,7 @@ use function collect; use function is_array; use function iterator_to_array; +use function json_encode; /** @method \MongoDB\Laravel\Query\Builder toBase() */ class Builder extends EloquentBuilder @@ -210,8 +211,8 @@ public function raw($value = null) */ public function createOrFirst(array $attributes = [], array $values = []): Model { - if ($attributes === []) { - throw new InvalidArgumentException('You must provide attributes to check for duplicates'); + if ($attributes === [] || $attributes === ['_id' => null]) { + throw new InvalidArgumentException('You must provide attributes to check for duplicates. Got ' . json_encode($attributes)); } // Apply casting and default values to the attributes @@ -233,8 +234,8 @@ public function createOrFirst(array $attributes = [], array $values = []): Model try { $document = $collection->findOneAndUpdate( $attributes, - // Before MongoDB 5.0, $setOnInsert requires a non-empty document. - // This should not be an issue as $values includes the query filter. + // Before MongoDB 5.0, $setOnInsert requires a non-empty document, + // this should not be an issue as $values include the query filter. ['$setOnInsert' => (object) $values], [ 'upsert' => true, diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 5974e49e1..ccae60915 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -746,6 +746,12 @@ protected function isBSON(mixed $value): bool */ public function save(array $options = []) { + // SQL databases would use autoincrement the id field if set to null. + // Apply the same behavior to MongoDB with _id only, otherwise null would be stored. + if (array_key_exists('_id', $this->attributes) && $this->attributes['_id'] === null) { + unset($this->attributes['_id']); + } + $saved = parent::save($options); // Clear list of unset fields diff --git a/tests/ModelTest.php b/tests/ModelTest.php index baa731799..ead5847ca 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -1151,4 +1151,21 @@ public function testUpdateOrCreate(array $criteria) $this->assertEquals($createdAt, $checkUser->created_at->getTimestamp()); $this->assertEquals($updatedAt, $checkUser->updated_at->getTimestamp()); } + + public function testCreateWithNullId() + { + $user = User::create(['_id' => null, 'email' => 'foo@bar']); + $this->assertNotNull(ObjectId::class, $user->id); + $this->assertSame(1, User::count()); + } + + public function testUpdateOrCreateWithNullId() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('You must provide attributes to check for duplicates'); + User::updateOrCreate( + ['_id' => null], + ['email' => 'jane.doe@example.com'], + ); + } } From 1c723bd2fb28cfa1be75cddc8dc75898d947a513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 28 May 2024 15:12:31 +0200 Subject: [PATCH 2/3] Revert comment changes --- src/Eloquent/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index 10cb94e67..678e7095b 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -234,8 +234,8 @@ public function createOrFirst(array $attributes = [], array $values = []): Model try { $document = $collection->findOneAndUpdate( $attributes, - // Before MongoDB 5.0, $setOnInsert requires a non-empty document, - // this should not be an issue as $values include the query filter. + // Before MongoDB 5.0, $setOnInsert requires a non-empty document. + // This should not be an issue as $values includes the query filter. ['$setOnInsert' => (object) $values], [ 'upsert' => true, From cf09ce91dd98a699f9a43710b3561215625fb859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 28 May 2024 16:03:29 +0200 Subject: [PATCH 3/3] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad748ef07..7de1b2cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [4.4.0] - unreleased * Support collection name prefix by @GromNaN in [#2930](https://github.com/mongodb/laravel-mongodb/pull/2930) +* Ignore `_id: null` to let MongoDB generate an `ObjectId` by @GromNaN in [#2969](https://github.com/mongodb/laravel-mongodb/pull/2969) * Add `mongodb` driver for Batching by @GromNaN in [#2904](https://github.com/mongodb/laravel-mongodb/pull/2904) * Rename queue option `table` to `collection` * Replace queue option `expire` with `retry_after`