Skip to content

Commit 48cd978

Browse files
committed
PHPORM-139 Implement Model::createOrFirst using findOneAndUpdate operation
1 parent ebaaf14 commit 48cd978

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/Eloquent/Builder.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
use Illuminate\Database\ConnectionInterface;
88
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
99
use MongoDB\Driver\Cursor;
10+
use MongoDB\Laravel\Collection;
1011
use MongoDB\Laravel\Helpers\QueriesRelationships;
1112
use MongoDB\Model\BSONDocument;
1213

14+
use function array_intersect_key;
1315
use function array_key_exists;
1416
use function array_merge;
1517
use function collect;
@@ -183,6 +185,22 @@ public function raw($value = null)
183185
return $results;
184186
}
185187

188+
public function createOrFirst(array $attributes = [], array $values = [])
189+
{
190+
// Apply casting and default values to the attributes
191+
$instance = $this->newModelInstance($values + $attributes);
192+
$values = $instance->getAttributes();
193+
$attributes = array_intersect_key($attributes, $values);
194+
195+
return $this->raw(function (Collection $collection) use ($attributes, $values) {
196+
return $collection->findOneAndUpdate(
197+
$attributes,
198+
['$setOnInsert' => $values],
199+
['upsert' => true, 'new' => true],
200+
);
201+
});
202+
}
203+
186204
/**
187205
* Add the "updated at" column to an array of values.
188206
* TODO Remove if https://github.com/laravel/framework/commit/6484744326531829341e1ff886cc9b628b20d73e

tests/ModelTest.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class ModelTest extends TestCase
4545
{
4646
public function tearDown(): void
4747
{
48-
User::truncate();
48+
User::raw(fn(Collection $collection) => $collection->drop());
4949
Soft::truncate();
5050
Book::truncate();
5151
Item::truncate();
@@ -1044,4 +1044,43 @@ public function testNumericFieldName(): void
10441044
$this->assertInstanceOf(User::class, $found);
10451045
$this->assertEquals([3 => 'two.three'], $found[2]);
10461046
}
1047+
1048+
public function testCreateOrFirst()
1049+
{
1050+
// Create index unique on "email" and "name"
1051+
User::raw(fn (Collection $collection) => $collection->createIndex(['email' => 1], ['unique' => true]));
1052+
User::raw(fn (Collection $collection) => $collection->createIndex(['name' => 1], ['unique' => true]));
1053+
1054+
$user1 = User::createOrFirst(['email' => '[email protected]']);
1055+
1056+
$this->assertSame('[email protected]', $user1->email);
1057+
$this->assertNull($user1->name);
1058+
1059+
$user2 = User::createOrFirst(
1060+
['email' => '[email protected]'],
1061+
['name' => 'Taylor Otwell', 'birthday' => new DateTime('1987-05-28')],
1062+
);
1063+
1064+
$this->assertEquals($user1->id, $user2->id);
1065+
$this->assertSame('[email protected]', $user2->email);
1066+
$this->assertNull($user2->name);
1067+
$this->assertNull($user2->birthday);
1068+
1069+
$user3 = User::createOrFirst(
1070+
['email' => '[email protected]'],
1071+
['name' => 'Abigail Otwell', 'birthday' => new DateTime('1987-05-28')],
1072+
);
1073+
1074+
$this->assertNotEquals($user3->id, $user1->id);
1075+
$this->assertSame('[email protected]', $user3->email);
1076+
$this->assertSame('Abigail Otwell', $user3->name);
1077+
$this->assertEquals(new DateTime('1987-05-28'), $user3->birthday);
1078+
1079+
$user4 = User::createOrFirst(
1080+
['name' => 'Dries Vints'],
1081+
['name' => 'Nuno Maduro', 'email' => '[email protected]'],
1082+
);
1083+
1084+
$this->assertSame('Nuno Maduro', $user4->name);
1085+
}
10471086
}

0 commit comments

Comments
 (0)