Skip to content

DOCSP-35947: write operations modify documents #2808

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
Show file tree
Hide file tree
Changes from all 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
164 changes: 136 additions & 28 deletions docs/fundamentals/write-operations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Write Operations
:values: tutorial

.. meta::
:keywords: insert, insert one, update, update one, upsert, delete, delete many, code example, mass assignment, eloquent model
:keywords: insert, insert one, code example, mass assignment, eloquent model

.. contents:: On this page
:local:
Expand All @@ -27,28 +27,15 @@ inserting, updating, and deleting data based on specified criteria.
This guide shows you how to perform the following tasks:

- :ref:`laravel-fundamentals-insert-documents`
- Modify Documents
- :ref:`laravel-fundamentals-modify-documents`
- Delete Documents

.. _laravel-fundamentals-insert-documents:

Insert Documents
----------------

In this section, you can learn how to insert documents into MongoDB collections
from your Laravel application by using the {+odm-long+}.

When you insert the documents, ensure the data does not violate any
unique indexes on the collection. When inserting the first document of a
collection or creating a new collection, MongoDB automatically creates a
unique index on the ``_id`` field.
.. _laravel-fundamentals-write-sample-model:

For more information on creating indexes on MongoDB collections by using the
Laravel schema builder, see the :ref:`laravel-eloquent-indexes` section
of the Schema Builder documentation.
Sample Model
~~~~~~~~~~~~

This section uses the following example model class to demonstrate how to
use Eloquent models to perform insert operations:
The write operations in this guide reference the following Eloquent model class:

.. literalinclude:: /includes/fundamentals/write-operations/Concert.php
:language: php
Expand All @@ -65,6 +52,23 @@ use Eloquent models to perform insert operations:
data types. To learn more, see `Attribute Casting <https://laravel.com/docs/{+laravel-docs-version+}/eloquent-mutators#attribute-casting>`__
in the Laravel documentation.

.. _laravel-fundamentals-insert-documents:

Insert Documents
----------------

In this section, you can learn how to insert documents into MongoDB collections
from your Laravel application by using the {+odm-long+}.

When you insert the documents, ensure the data does not violate any
unique indexes on the collection. When inserting the first document of a
collection or creating a new collection, MongoDB automatically creates a
unique index on the ``_id`` field.

For more information on creating indexes on MongoDB collections by using the
Laravel schema builder, see the :ref:`laravel-eloquent-indexes` section
of the Schema Builder documentation.

To learn more about Eloquent models in {+odm-short+}, see the :ref:`laravel-eloquent-models`
section.

Expand All @@ -75,13 +79,16 @@ These examples show how to use the ``save()`` Eloquent method to insert an
instance of a ``Concert`` model as a MongoDB document.

When the ``save()`` method succeeds, you can access the model instance on
which you called the method. If the operation fails, the model instance is
assigned ``null``.
which you called the method.

If the operation fails, the model instance is assigned ``null``.

This example code performs the following actions:

- Creates a new instance of the ``Concert`` model
- Assigns string values to the ``performer`` and ``venue`` fields
- Assigns an array of strings to the ``genre`` field
- Assigns a number to the ``ticketsSold`` field
- Assigns a date to the ``performanceDate`` field by using the ``Carbon``
package
- Inserts the document by calling the ``save()`` method
Expand All @@ -93,7 +100,7 @@ This example code performs the following actions:
:end-before: end model insert one

You can retrieve the inserted document's ``_id`` value by accessing the model's
``id`` member as shown in the following code example:
``id`` member, as shown in the following code example:

.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
Expand All @@ -103,7 +110,7 @@ You can retrieve the inserted document's ``_id`` value by accessing the model's

If you enable mass assignment by defining either the ``$fillable`` or
``$guarded`` attributes, you can use the Eloquent model ``create()`` method
to perform the insert in a single call as shown in the following example:
to perform the insert in a single call, as shown in the following example:

.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
Expand All @@ -122,21 +129,122 @@ multiple instances of a ``Concert`` model as MongoDB documents. This bulk
insert method reduces the number of calls your application needs to make
to save the documents.

When the ``insert()`` method succeeds, it returns the value ``1``. If it
fails, it throws an exception.

When the ``insert()`` method succeeds, it returns the value ``1``.

If it fails, it throws an exception.

The example code saves multiple models in a single call by passing them as
an array to the ``insert()`` method:

.. note::

This example wraps the dates in the `MongoDB\BSON\UTCDateTime <{+phplib-api+}/class.mongodb-bson-utcdatetime.php>`__
class to convert it to a type MongoDB can serialize because Laravel
skips attribute casting on bulk insert operations.
This example wraps the dates in the `MongoDB\\BSON\\UTCDateTime <{+phplib-api+}/class.mongodb-bson-utcdatetime.php>`__
class to convert it to a type MongoDB can serialize because Laravel
skips attribute casting on bulk insert operations.

.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model insert many
:end-before: end model insert many

.. _laravel-fundamentals-modify-documents:

Modify Documents
----------------

In this section, you can learn how to modify documents in your MongoDB
collection from your Laravel application. Use update operations to modify
existing documents or to insert a document if none match the search criteria.

You can persist changes on an instance of an Eloquent model or use
Eloquent's fluent syntax to chain an update operation on methods that
return a Laravel collection object.

This section provides examples of the following update operations:

- :ref:`Update a document <laravel-modify-documents-update-one>`
- :ref:`Update multiple documents <laravel-modify-documents-update-multiple>`

.. _laravel-modify-documents-update-one:

Update a Document Examples
~~~~~~~~~~~~~~~~~~~~~~~~~~

You can update a document in the following ways:

- Modify an instance of the model and save the changes by calling the ``save()``
method.
- Chain methods to retrieve an instance of a model and perform updates on it
by calling the ``update()`` method.

The following example shows how to update a document by modifying an instance
of the model and calling its ``save()`` method:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: not necessary, but I like the detailed description of the code example in lines 86-94. Could be helpful to introduce this code example with more specific info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there specific info that you think might be helpful to add? I think the insert example adds type information which sets up the rest of the examples on the page, but could be repetitive and draw away from the focus of the rest of the examples.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of specifying which fields / values you're updating (venue, ticketsSold), but agreed that this could be repetitive and I think it's fine as is!


.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model update one save
:end-before: end model update one save

When the ``save()`` method succeeds, the model instance on which you called the
method contains the updated values.

If the operation fails, {+odm-short+} assigns the model instance a null value.

The following example shows how to update a document by chaining methods to
retrieve and update the first matching document:

.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model update one fluent
:end-before: end model update one fluent

.. note::

The ``orderBy()`` call sorts the results by the ``_id`` field to
guarantee a consistent sort order. To learn more about sorting in MongoDB,
see the :manual:`Natural order </reference/glossary/#std-term-natural-order>`
glossary entry in the {+server-docs-name+}.

When the ``update()`` method succeeds, the operation returns the number of
documents updated.

If the retrieve part of the call does not match any documents, {+odm-short+}
returns the following error:

.. code-block:: none
:copyable: false

Error: Call to a member function update() on null

.. _laravel-modify-documents-update-multiple:

Update Multiple Documents Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To perform an update on one or more documents, chain the ``update()``
method to the results of a method that retrieves the documents as a
Laravel collection object, such as ``where()``.

The following example shows how to chain calls to retrieve matching documents
and update them:

.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php
:language: php
:dedent:
:start-after: begin model update multiple
:end-before: end model update multiple

When the ``update()`` method succeeds, the operation returns the number of
documents updated.

If the retrieve part of the call does not match any documents in MongoDB,
{+odm-short+} returns the following error:

.. code-block:: none
:copyable: false

Error: Call to a member function update() on null
2 changes: 1 addition & 1 deletion docs/includes/fundamentals/write-operations/Concert.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
class Concert extends Model
{
protected $connection = 'mongodb';
protected $fillable = ['performer', 'venue', 'performanceDate'];
protected $fillable = ['performer', 'venue', 'genres', 'ticketsSold', 'performanceDate'];
protected $casts = ['performanceDate' => 'datetime'];
}
117 changes: 117 additions & 0 deletions docs/includes/fundamentals/write-operations/WriteOperationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function testModelInsert(): void
$concert = new Concert();
$concert->performer = 'Mitsuko Uchida';
$concert->venue = 'Carnegie Hall';
$concert->genres = ['classical'];
$concert->ticketsSold = 2121;
$concert->performanceDate = Carbon::create(2024, 4, 1, 20, 0, 0, 'EST');
$concert->save();
// end model insert one
Expand Down Expand Up @@ -56,6 +58,8 @@ public function testModelInsertMassAssign(): void
$insertResult = Concert::create([
'performer' => 'The Rolling Stones',
'venue' => 'Soldier Field',
'genres' => [ 'rock', 'pop', 'blues' ],
'ticketsSold' => 59527,
'performanceDate' => Carbon::create(2024, 6, 30, 20, 0, 0, 'CDT'),
]);
// end model insert one mass assign
Expand All @@ -81,11 +85,15 @@ public function testModelInsertMany(): void
[
'performer' => 'Brad Mehldau',
'venue' => 'Philharmonie de Paris',
'genres' => [ 'jazz', 'post-bop' ],
'ticketsSold' => 5745,
'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')),
],
[
'performer' => 'Billy Joel',
'venue' => 'Madison Square Garden',
'genres' => [ 'rock', 'soft rock', 'pop rock' ],
'ticketsSold' => 12852,
'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')),
],
];
Expand All @@ -97,4 +105,113 @@ public function testModelInsertMany(): void

$this->assertEquals(2, count($results));
}

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

// insert the model
Concert::create([
'performer' => 'Brad Mehldau',
'venue' => 'Philharmonie de Paris',
'genres' => [ 'jazz', 'post-bop' ],
'ticketsSold' => 5745,
'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')),
]);

// begin model update one save
$concert = Concert::first();
$concert->venue = 'Manchester Arena';
$concert->ticketsSold = 9543;
$concert->save();
// end model update one save

$result = Concert::first();
$this->assertInstanceOf(Concert::class, $result);

$this->assertNotNull($result);
$this->assertEquals('Manchester Arena', $result->venue);
$this->assertEquals('Brad Mehldau', $result->performer);
$this->assertEquals(9543, $result->ticketsSold);
}

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

// insert the model
Concert::create([
'performer' => 'Brad Mehldau',
'venue' => 'Philharmonie de Paris',
'genres' => [ 'jazz', 'post-bop' ],
'ticketsSold' => 5745,
'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')),
]);

// begin model update one fluent
$concert = Concert::where(['performer' => 'Brad Mehldau'])
->orderBy('_id')
->first()
->update(['venue' => 'Manchester Arena', 'ticketsSold' => 9543]);
// end model update one fluent

$result = Concert::first();
$this->assertInstanceOf(Concert::class, $result);

$this->assertNotNull($result);
$this->assertEquals('Manchester Arena', $result->venue);
$this->assertEquals('Brad Mehldau', $result->performer);
$this->assertEquals(9543, $result->ticketsSold);
}

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

// insert the model
Concert::create([
'performer' => 'Brad Mehldau',
'venue' => 'Philharmonie de Paris',
'genres' => [ 'jazz', 'post-bop' ],
'ticketsSold' => 5745,
'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')),
]);

Concert::create([
'performer' => 'The Rolling Stones',
'venue' => 'Soldier Field',
'genres' => [ 'rock', 'pop', 'blues' ],
'ticketsSold' => 59527,
'performanceDate' => Carbon::create(2024, 6, 30, 20, 0, 0, 'CDT'),
]);
// begin model update multiple
Concert::whereIn('venue', ['Philharmonie de Paris', 'Soldier Field'])
->update(['venue' => 'Concertgebouw', 'ticketsSold' => 0]);
// end model update multiple

$results = Concert::get();

foreach ($results as $result) {
$this->assertInstanceOf(Concert::class, $result);

$this->assertNotNull($result);
$this->assertEquals('Concertgebouw', $result->venue);
$this->assertEquals(0, $result->ticketsSold);
}
}
}