Skip to content

Commit f5f86c8

Browse files
authored
PHPORM-139 Improve Model::createOrFirst() tests and error checking (#2759)
1 parent c8fb0a0 commit f5f86c8

File tree

2 files changed

+35
-14
lines changed

2 files changed

+35
-14
lines changed

src/Eloquent/Builder.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
use Illuminate\Database\ConnectionInterface;
88
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
9+
use InvalidArgumentException;
910
use MongoDB\Driver\Cursor;
1011
use MongoDB\Laravel\Collection;
1112
use MongoDB\Laravel\Helpers\QueriesRelationships;
1213
use MongoDB\Laravel\Internal\FindAndModifyCommandSubscriber;
1314
use MongoDB\Model\BSONDocument;
15+
use MongoDB\Operation\FindOneAndUpdate;
1416

1517
use function array_intersect_key;
1618
use function array_key_exists;
@@ -195,7 +197,12 @@ public function raw($value = null)
195197
*/
196198
public function createOrFirst(array $attributes = [], array $values = []): Model
197199
{
200+
if ($attributes === []) {
201+
throw new InvalidArgumentException('You must provide attributes to check for duplicates');
202+
}
203+
198204
// Apply casting and default values to the attributes
205+
// In case of duplicate key between the attributes and the values, the values have priority
199206
$instance = $this->newModelInstance($values + $attributes);
200207
$values = $instance->getAttributes();
201208
$attributes = array_intersect_key($attributes, $values);
@@ -207,8 +214,14 @@ public function createOrFirst(array $attributes = [], array $values = []): Model
207214
try {
208215
$document = $collection->findOneAndUpdate(
209216
$attributes,
210-
['$setOnInsert' => $values],
211-
['upsert' => true, 'new' => true, 'typeMap' => ['root' => 'array', 'document' => 'array']],
217+
// Before MongoDB 5.0, $setOnInsert requires a non-empty document.
218+
// This is should not be an issue as $values includes the query filter.
219+
['$setOnInsert' => (object) $values],
220+
[
221+
'upsert' => true,
222+
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
223+
'typeMap' => ['root' => 'array', 'document' => 'array'],
224+
],
212225
);
213226
} finally {
214227
$collection->getManager()->removeSubscriber($listener);

tests/ModelTest.php

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Illuminate\Database\Eloquent\ModelNotFoundException;
1212
use Illuminate\Support\Facades\Date;
1313
use Illuminate\Support\Str;
14+
use InvalidArgumentException;
1415
use MongoDB\BSON\Binary;
1516
use MongoDB\BSON\ObjectID;
1617
use MongoDB\BSON\UTCDateTime;
@@ -1047,40 +1048,47 @@ public function testNumericFieldName(): void
10471048

10481049
public function testCreateOrFirst()
10491050
{
1050-
$user1 = User::createOrFirst(['email' => 'taylorotwell@gmail.com']);
1051+
$user1 = User::createOrFirst(['email' => 'john.doe@example.com']);
10511052

1052-
$this->assertSame('taylorotwell@gmail.com', $user1->email);
1053+
$this->assertSame('john.doe@example.com', $user1->email);
10531054
$this->assertNull($user1->name);
10541055
$this->assertTrue($user1->wasRecentlyCreated);
10551056

10561057
$user2 = User::createOrFirst(
1057-
['email' => 'taylorotwell@gmail.com'],
1058-
['name' => 'Taylor Otwell', 'birthday' => new DateTime('1987-05-28')],
1058+
['email' => 'john.doe@example.com'],
1059+
['name' => 'John Doe', 'birthday' => new DateTime('1987-05-28')],
10591060
);
10601061

10611062
$this->assertEquals($user1->id, $user2->id);
1062-
$this->assertSame('taylorotwell@gmail.com', $user2->email);
1063+
$this->assertSame('john.doe@example.com', $user2->email);
10631064
$this->assertNull($user2->name);
10641065
$this->assertNull($user2->birthday);
10651066
$this->assertFalse($user2->wasRecentlyCreated);
10661067

10671068
$user3 = User::createOrFirst(
1068-
['email' => 'abigailotwell@gmail.com'],
1069-
['name' => 'Abigail Otwell', 'birthday' => new DateTime('1987-05-28')],
1069+
['email' => 'jane.doe@example.com'],
1070+
['name' => 'Jane Doe', 'birthday' => new DateTime('1987-05-28')],
10701071
);
10711072

10721073
$this->assertNotEquals($user3->id, $user1->id);
1073-
$this->assertSame('abigailotwell@gmail.com', $user3->email);
1074-
$this->assertSame('Abigail Otwell', $user3->name);
1074+
$this->assertSame('jane.doe@example.com', $user3->email);
1075+
$this->assertSame('Jane Doe', $user3->name);
10751076
$this->assertEquals(new DateTime('1987-05-28'), $user3->birthday);
10761077
$this->assertTrue($user3->wasRecentlyCreated);
10771078

10781079
$user4 = User::createOrFirst(
1079-
['name' => 'Dries Vints'],
1080-
['name' => 'Nuno Maduro', 'email' => 'nuno@laravel.com'],
1080+
['name' => 'Robert Doe'],
1081+
['name' => 'Maria Doe', 'email' => 'maria.doe@example.com'],
10811082
);
10821083

1083-
$this->assertSame('Nuno Maduro', $user4->name);
1084+
$this->assertSame('Maria Doe', $user4->name);
10841085
$this->assertTrue($user4->wasRecentlyCreated);
10851086
}
1087+
1088+
public function testCreateOrFirstRequiresFilter()
1089+
{
1090+
$this->expectException(InvalidArgumentException::class);
1091+
$this->expectExceptionMessage('You must provide attributes to check for duplicates');
1092+
User::createOrFirst([]);
1093+
}
10861094
}

0 commit comments

Comments
 (0)