Skip to content

DOCSP-41621: upsert #3089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/feature-compatibility.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ The following Eloquent methods are not supported in {+odm-short+}:
- *Unsupported as MongoDB uses ObjectIDs*

* - Upserts
- *Unsupported*
- ✓ See :ref:`laravel-mongodb-query-builder-upsert`.

* - Update Statements
- ✓
Expand Down Expand Up @@ -216,7 +216,7 @@ Eloquent Features
- ✓

* - Upserts
- *Unsupported, but you can use the createOneOrFirst() method*
- ✓ See :ref:`laravel-modify-documents-upsert`.

* - Deleting Models
- ✓
Expand Down
87 changes: 83 additions & 4 deletions docs/fundamentals/write-operations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,85 @@ An **upsert** operation lets you perform an update or insert in a single
operation. This operation streamlines the task of updating a document or
inserting one if it does not exist.

Starting in v4.7, you can perform an upsert operation by using either of
the following methods:

- Use the ``upsert()`` method. When you use this method,
you can perform a **batch upsert** to change or insert multiple documents
in one operation.

- Use the ``update()`` method and specify the ``upsert``
option to update all documents that match the query filter or insert
one document if no documents are matched. Only this method is
supported in versions v4.6 and earlier.

Upsert Method
~~~~~~~~~~~~~

The ``upsert()`` query builder method accepts the following parameters:

- Array of fields and values that specify documents to update or insert.
- Array of fields that uniquely identify documents in your first array parameter.
- Array of fields to update if a matching document exists. If you omit
this parameter, {+odm-short+} updates all fields.

To specify an upsert in the ``upsert()`` method, set parameters
as shown in the following code example:

.. code-block:: php
:copyable: false

YourModel::upsert(
[/* documents to update or insert */],
'/* unique field */', [/* fields to update */]
);

Example
^^^^^^^

This example shows how to use the ``upsert()``
method to perform an update or insert in a single operation. Click the
:guilabel:`{+code-output-label+}` button to see the resulting data changes if
there is an ``'Angel Olson'`` document in the collection already:

.. io-code-block::

.. input:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model upsert
:end-before: end model upsert

.. output::
:language: json
:visible: false

{
"_id": "...",
"performer": "Angel Olsen",
"venue": "State Theatre",
"genres": [
"indie",
"rock"
],
"ticketsSold": 275,
"updated_at": ...
},
{
"_id": "...",
"performer": "Darondo",
"venue": "Cafe du Nord",
"ticketsSold": 300,
"updated_at": ...
}

In the ``'Angel Olsen'`` document, the ``venue`` field value is not
changed, as the upsert specifies that the update only applies to the
``ticketsSold`` field.

Update Method
~~~~~~~~~~~~~

To specify an upsert in an ``update()`` method, set the ``upsert`` option to
``true`` as shown in the following code example:

Expand All @@ -278,8 +357,8 @@ following actions:
- If the query matches zero documents, the ``update()`` method inserts a
document that contains the update data and the equality match criteria data.

Upsert Example
~~~~~~~~~~~~~~
Example
^^^^^^^

This example shows how to pass the ``upsert`` option to the ``update()``
method to perform an update or insert in a single operation. Click the
Expand All @@ -291,8 +370,8 @@ matching documents exist:
.. input:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model upsert
:end-before: end model upsert
:start-after: begin model update upsert
:end-before: end model update upsert

.. output::
:language: json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,22 +217,55 @@ public function testModelUpdateMultiple(): void
}
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testModelUpsert(): void
{
require_once __DIR__ . '/Concert.php';
Concert::truncate();

// Pre-existing sample document
Concert::create([
'performer' => 'Angel Olsen',
'venue' => 'State Theatre',
'genres' => [ 'indie', 'rock' ],
'ticketsSold' => 150,
]);

// begin model upsert
Concert::upsert([
['performer' => 'Angel Olsen', 'venue' => 'Academy of Music', 'ticketsSold' => 275],
['performer' => 'Darondo', 'venue' => 'Cafe du Nord', 'ticketsSold' => 300],
], 'performer', ['ticketsSold']);
// end model upsert

$this->assertSame(2, Concert::count());

$this->assertSame(275, Concert::where('performer', 'Angel Olsen')->first()->ticketsSold);
$this->assertSame('State Theatre', Concert::where('performer', 'Angel Olsen')->first()->venue);

$this->assertSame(300, Concert::where('performer', 'Darondo')->first()->ticketsSold);
$this->assertSame('Cafe du Nord', Concert::where('performer', 'Darondo')->first()->venue);
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testModelUpsert(): void
public function testModelUpdateUpsert(): void
{
require_once __DIR__ . '/Concert.php';
Concert::truncate();

// begin model upsert
// begin model update upsert
Concert::where(['performer' => 'Jon Batiste', 'venue' => 'Radio City Music Hall'])
->update(
['genres' => ['R&B', 'soul'], 'ticketsSold' => 4000],
['upsert' => true],
);
// end model upsert
// end model update upsert

$result = Concert::first();

Expand Down
25 changes: 24 additions & 1 deletion docs/includes/query-builder/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,29 @@ function (Collection $collection) {
public function testUpsert(): void
{
// begin upsert
$result = DB::collection('movies')
->upsert(
[
['title' => 'Inspector Maigret', 'recommended' => false, 'runtime' => 128],
['title' => 'Petit Maman', 'recommended' => true, 'runtime' => 72],
],
'title',
'recommended',
);
// end upsert

$this->assertSame(2, $result);

$this->assertSame(119, DB::collection('movies')->where('title', 'Inspector Maigret')->first()['runtime']);
$this->assertSame(false, DB::collection('movies')->where('title', 'Inspector Maigret')->first()['recommended']);

$this->assertSame(true, DB::collection('movies')->where('title', 'Petit Maman')->first()['recommended']);
$this->assertSame(72, DB::collection('movies')->where('title', 'Petit Maman')->first()['runtime']);
}

public function testUpdateUpsert(): void
{
// begin update upsert
$result = DB::collection('movies')
->where('title', 'Will Hunting')
->update(
Expand All @@ -516,7 +539,7 @@ public function testUpsert(): void
],
['upsert' => true],
);
// end upsert
// end update upsert

$this->assertIsInt($result);
}
Expand Down
Loading
Loading