Skip to content

Commit 8ca5940

Browse files
committed
Unset _id null to let it be autogenerated
1 parent e8a8902 commit 8ca5940

File tree

3 files changed

+28
-4
lines changed

3 files changed

+28
-4
lines changed

src/Eloquent/Builder.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use function collect;
2222
use function is_array;
2323
use function iterator_to_array;
24+
use function json_encode;
2425

2526
/** @method \MongoDB\Laravel\Query\Builder toBase() */
2627
class Builder extends EloquentBuilder
@@ -210,8 +211,8 @@ public function raw($value = null)
210211
*/
211212
public function createOrFirst(array $attributes = [], array $values = []): Model
212213
{
213-
if ($attributes === []) {
214-
throw new InvalidArgumentException('You must provide attributes to check for duplicates');
214+
if ($attributes === [] || $attributes === ['_id' => null]) {
215+
throw new InvalidArgumentException('You must provide attributes to check for duplicates. Got ' . json_encode($attributes));
215216
}
216217

217218
// Apply casting and default values to the attributes
@@ -233,8 +234,8 @@ public function createOrFirst(array $attributes = [], array $values = []): Model
233234
try {
234235
$document = $collection->findOneAndUpdate(
235236
$attributes,
236-
// Before MongoDB 5.0, $setOnInsert requires a non-empty document.
237-
// This should not be an issue as $values includes the query filter.
237+
// Before MongoDB 5.0, $setOnInsert requires a non-empty document,
238+
// this should not be an issue as $values include the query filter.
238239
['$setOnInsert' => (object) $values],
239240
[
240241
'upsert' => true,

src/Eloquent/Model.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,12 @@ protected function isBSON(mixed $value): bool
747747
*/
748748
public function save(array $options = [])
749749
{
750+
// SQL databases would use autoincrement the id field if set to null.
751+
// Apply the same behavior to MongoDB with _id only, otherwise null would be stored.
752+
if (array_key_exists('_id', $this->attributes) && $this->attributes['_id'] === null) {
753+
unset($this->attributes['_id']);
754+
}
755+
750756
$saved = parent::save($options);
751757

752758
// Clear list of unset fields

tests/ModelTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,4 +1151,21 @@ public function testUpdateOrCreate(array $criteria)
11511151
$this->assertEquals($createdAt, $checkUser->created_at->getTimestamp());
11521152
$this->assertEquals($updatedAt, $checkUser->updated_at->getTimestamp());
11531153
}
1154+
1155+
public function testCreateWithNullId()
1156+
{
1157+
$user = User::create(['_id' => null, 'email' => 'foo@bar']);
1158+
$this->assertNotNull(ObjectId::class, $user->id);
1159+
$this->assertSame(1, User::count());
1160+
}
1161+
1162+
public function testUpdateOrCreateWithNullId()
1163+
{
1164+
$this->expectException(InvalidArgumentException::class);
1165+
$this->expectExceptionMessage('You must provide attributes to check for duplicates');
1166+
User::updateOrCreate(
1167+
['_id' => null],
1168+
['email' => 'jane.doe@example.com'],
1169+
);
1170+
}
11541171
}

0 commit comments

Comments
 (0)