|
7 | 7 | use Illuminate\Database\ConnectionInterface; |
8 | 8 | use Illuminate\Database\Eloquent\Builder as EloquentBuilder; |
9 | 9 | use MongoDB\Driver\Cursor; |
| 10 | +use MongoDB\Laravel\Collection; |
10 | 11 | use MongoDB\Laravel\Helpers\QueriesRelationships; |
| 12 | +use MongoDB\Laravel\Internal\FindAndModifyCommandSubscriber; |
11 | 13 | use MongoDB\Model\BSONDocument; |
12 | 14 |
|
| 15 | +use function array_intersect_key; |
13 | 16 | use function array_key_exists; |
14 | 17 | use function array_merge; |
15 | 18 | use function collect; |
@@ -183,6 +186,41 @@ public function raw($value = null) |
183 | 186 | return $results; |
184 | 187 | } |
185 | 188 |
|
| 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 |
| 197 | + { |
| 198 | + // Apply casting and default values to the attributes |
| 199 | + $instance = $this->newModelInstance($values + $attributes); |
| 200 | + $values = $instance->getAttributes(); |
| 201 | + $attributes = array_intersect_key($attributes, $values); |
| 202 | + |
| 203 | + return $this->raw(function (Collection $collection) use ($attributes, $values) { |
| 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; |
| 221 | + }); |
| 222 | + } |
| 223 | + |
186 | 224 | /** |
187 | 225 | * Add the "updated at" column to an array of values. |
188 | 226 | * TODO Remove if https://github.com/laravel/framework/commit/6484744326531829341e1ff886cc9b628b20d73e |
|
0 commit comments