Skip to content

Commit 0eec67b

Browse files
committed
Use monitoring to track if model was created or found
1 parent 48cd978 commit 0eec67b

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

Diff for: src/Eloquent/Builder.php

+26-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use MongoDB\Driver\Cursor;
1010
use MongoDB\Laravel\Collection;
1111
use MongoDB\Laravel\Helpers\QueriesRelationships;
12+
use MongoDB\Laravel\Internal\FindAndModifyCommandSubscriber;
1213
use MongoDB\Model\BSONDocument;
1314

1415
use function array_intersect_key;
@@ -185,19 +186,38 @@ public function raw($value = null)
185186
return $results;
186187
}
187188

188-
public function createOrFirst(array $attributes = [], array $values = [])
189+
/**
190+
* Attempt to create the record if it does not exist with the matching attributes.
191+
* If the record exists, it will be returned.
192+
*
193+
* @param array $attributes The attributes to check for duplicate records
194+
* @param array $values The attributes to insert if no matching record is found
195+
*/
196+
public function createOrFirst(array $attributes = [], array $values = []): Model
189197
{
190198
// Apply casting and default values to the attributes
191199
$instance = $this->newModelInstance($values + $attributes);
192200
$values = $instance->getAttributes();
193201
$attributes = array_intersect_key($attributes, $values);
194202

195203
return $this->raw(function (Collection $collection) use ($attributes, $values) {
196-
return $collection->findOneAndUpdate(
197-
$attributes,
198-
['$setOnInsert' => $values],
199-
['upsert' => true, 'new' => true],
200-
);
204+
$listener = new FindAndModifyCommandSubscriber();
205+
$collection->getManager()->addSubscriber($listener);
206+
207+
try {
208+
$document = $collection->findOneAndUpdate(
209+
$attributes,
210+
['$setOnInsert' => $values],
211+
['upsert' => true, 'new' => true, 'typeMap' => ['root' => 'array', 'document' => 'array']],
212+
);
213+
} finally {
214+
$collection->getManager()->removeSubscriber($listener);
215+
}
216+
217+
$model = $this->model->newFromBuilder($document);
218+
$model->wasRecentlyCreated = $listener->created;
219+
220+
return $model;
201221
});
202222
}
203223

Diff for: src/Internal/FindAndModifyCommandSubscriber.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Internal;
6+
7+
use MongoDB\Driver\Monitoring\CommandFailedEvent;
8+
use MongoDB\Driver\Monitoring\CommandStartedEvent;
9+
use MongoDB\Driver\Monitoring\CommandSubscriber;
10+
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
11+
12+
/**
13+
* Track findAndModify command events to detect when a document is inserted or
14+
* updated.
15+
*
16+
* @internal
17+
*/
18+
class FindAndModifyCommandSubscriber implements CommandSubscriber
19+
{
20+
public bool $created;
21+
22+
public function commandFailed(CommandFailedEvent $event)
23+
{
24+
}
25+
26+
public function commandStarted(CommandStartedEvent $event)
27+
{
28+
}
29+
30+
public function commandSucceeded(CommandSucceededEvent $event)
31+
{
32+
$this->created = ! $event->getReply()->lastErrorObject->updatedExisting;
33+
}
34+
}

Diff for: tests/ModelTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,7 @@ public function testCreateOrFirst()
10551055

10561056
$this->assertSame('[email protected]', $user1->email);
10571057
$this->assertNull($user1->name);
1058+
$this->assertTrue($user1->wasRecentlyCreated);
10581059

10591060
$user2 = User::createOrFirst(
10601061
['email' => '[email protected]'],
@@ -1065,6 +1066,7 @@ public function testCreateOrFirst()
10651066
$this->assertSame('[email protected]', $user2->email);
10661067
$this->assertNull($user2->name);
10671068
$this->assertNull($user2->birthday);
1069+
$this->assertFalse($user2->wasRecentlyCreated);
10681070

10691071
$user3 = User::createOrFirst(
10701072
['email' => '[email protected]'],
@@ -1075,12 +1077,14 @@ public function testCreateOrFirst()
10751077
$this->assertSame('[email protected]', $user3->email);
10761078
$this->assertSame('Abigail Otwell', $user3->name);
10771079
$this->assertEquals(new DateTime('1987-05-28'), $user3->birthday);
1080+
$this->assertTrue($user3->wasRecentlyCreated);
10781081

10791082
$user4 = User::createOrFirst(
10801083
['name' => 'Dries Vints'],
10811084
['name' => 'Nuno Maduro', 'email' => '[email protected]'],
10821085
);
10831086

10841087
$this->assertSame('Nuno Maduro', $user4->name);
1088+
$this->assertTrue($user4->wasRecentlyCreated);
10851089
}
10861090
}

0 commit comments

Comments
 (0)