Skip to content

Commit

Permalink
PHPORM-139 Improve Model::createOrFirst() tests and error checking (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
GromNaN authored Mar 13, 2024
1 parent c8fb0a0 commit f5f86c8
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 14 deletions.
17 changes: 15 additions & 2 deletions src/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use InvalidArgumentException;
use MongoDB\Driver\Cursor;
use MongoDB\Laravel\Collection;
use MongoDB\Laravel\Helpers\QueriesRelationships;
use MongoDB\Laravel\Internal\FindAndModifyCommandSubscriber;
use MongoDB\Model\BSONDocument;
use MongoDB\Operation\FindOneAndUpdate;

use function array_intersect_key;
use function array_key_exists;
Expand Down Expand Up @@ -195,7 +197,12 @@ 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');
}

// Apply casting and default values to the attributes
// In case of duplicate key between the attributes and the values, the values have priority
$instance = $this->newModelInstance($values + $attributes);
$values = $instance->getAttributes();
$attributes = array_intersect_key($attributes, $values);
Expand All @@ -207,8 +214,14 @@ public function createOrFirst(array $attributes = [], array $values = []): Model
try {
$document = $collection->findOneAndUpdate(
$attributes,
['$setOnInsert' => $values],
['upsert' => true, 'new' => true, 'typeMap' => ['root' => 'array', 'document' => 'array']],
// Before MongoDB 5.0, $setOnInsert requires a non-empty document.
// This is should not be an issue as $values includes the query filter.
['$setOnInsert' => (object) $values],
[
'upsert' => true,
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
'typeMap' => ['root' => 'array', 'document' => 'array'],
],
);
} finally {
$collection->getManager()->removeSubscriber($listener);
Expand Down
32 changes: 20 additions & 12 deletions tests/ModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Str;
use InvalidArgumentException;
use MongoDB\BSON\Binary;
use MongoDB\BSON\ObjectID;
use MongoDB\BSON\UTCDateTime;
Expand Down Expand Up @@ -1047,40 +1048,47 @@ public function testNumericFieldName(): void

public function testCreateOrFirst()
{
$user1 = User::createOrFirst(['email' => 'taylorotwell@gmail.com']);
$user1 = User::createOrFirst(['email' => 'john.doe@example.com']);

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

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

$this->assertEquals($user1->id, $user2->id);
$this->assertSame('taylorotwell@gmail.com', $user2->email);
$this->assertSame('john.doe@example.com', $user2->email);
$this->assertNull($user2->name);
$this->assertNull($user2->birthday);
$this->assertFalse($user2->wasRecentlyCreated);

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

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

$user4 = User::createOrFirst(
['name' => 'Dries Vints'],
['name' => 'Nuno Maduro', 'email' => 'nuno@laravel.com'],
['name' => 'Robert Doe'],
['name' => 'Maria Doe', 'email' => 'maria.doe@example.com'],
);

$this->assertSame('Nuno Maduro', $user4->name);
$this->assertSame('Maria Doe', $user4->name);
$this->assertTrue($user4->wasRecentlyCreated);
}

public function testCreateOrFirstRequiresFilter()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('You must provide attributes to check for duplicates');
User::createOrFirst([]);
}
}

0 comments on commit f5f86c8

Please sign in to comment.