diff --git a/docs/fundamentals.txt b/docs/fundamentals.txt new file mode 100644 index 000000000..041388350 --- /dev/null +++ b/docs/fundamentals.txt @@ -0,0 +1,25 @@ +.. _laravel_fundamentals: + +============ +Fundamentals +============ + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: php framework, odm + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + /fundamentals/read-operations + /fundamentals/write-operations + +Learn how to use the {+odm-long+} to perform the following tasks: + +- :ref:`Read Operations ` +- :ref:`Write Operations ` + diff --git a/docs/retrieve.txt b/docs/fundamentals/read-operations.txt similarity index 99% rename from docs/retrieve.txt rename to docs/fundamentals/read-operations.txt index a295c0bb8..e65e32d88 100644 --- a/docs/retrieve.txt +++ b/docs/fundamentals/read-operations.txt @@ -1,8 +1,9 @@ .. _laravel-fundamentals-retrieve: +.. _laravel-fundamentals-read-ops: -============== -Retrieve Data -============== +=============== +Read Operations +=============== .. facet:: :name: genre diff --git a/docs/fundamentals/write-operations.txt b/docs/fundamentals/write-operations.txt new file mode 100644 index 000000000..242d4e941 --- /dev/null +++ b/docs/fundamentals/write-operations.txt @@ -0,0 +1,616 @@ +.. _laravel-fundamentals-write-ops: + +================ +Write Operations +================ + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: insert, insert one, update, update one, upsert, code example, mass assignment, push, pull, delete, delete many, primary key, destroy, eloquent model + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use {+odm-short+} to perform +**write operations** on your MongoDB collections. Write operations include +inserting, updating, and deleting data based on specified criteria. + +This guide shows you how to perform the following tasks: + +- :ref:`laravel-fundamentals-insert-documents` +- :ref:`laravel-fundamentals-modify-documents` +- :ref:`laravel-fundamentals-delete-documents` + +.. _laravel-fundamentals-write-sample-model: + +Sample Model +~~~~~~~~~~~~ + +The write operations in this guide reference the following Eloquent model class: + +.. literalinclude:: /includes/fundamentals/write-operations/Concert.php + :language: php + :dedent: + :caption: Concert.php + +.. tip:: + + The ``$fillable`` attribute lets you use Laravel mass assignment for insert + operations. To learn more about mass assignment, see :ref:`laravel-model-mass-assignment` + in the Eloquent Model Class documentation. + + The ``$casts`` attribute instructs Laravel to convert attributes to common + data types. To learn more, see `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. + +Insert a Document Examples +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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``. + +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 + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model insert one + :end-before: end model insert one + :caption: Insert a document by calling the save() method on an instance. + +You can retrieve the inserted document's ``_id`` value by accessing the model's +``id`` member, as shown in the following code example: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin inserted id + :end-before: end inserted id + +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: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model insert one mass assign + :end-before: end model insert one mass assign + +To learn more about the Carbon PHP API extension, see the +:github:`Carbon ` GitHub repository. + +Insert Multiple Documents Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example shows how to use the ``insert()`` Eloquent method to insert +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. + +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. + +.. 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 ` +- :ref:`Update multiple documents ` +- :ref:`Update or insert in a single operation ` +- :ref:`Update arrays in a document ` + +.. _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: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model update one save + :end-before: end model update one save + :caption: Update a document by calling the save() method on an instance. + +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 + :caption: Update the matching document by chaining the update() method. + +.. include:: /includes/fact-orderby-id.rst + +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 the +collection, {+odm-short+} returns the following error: + +.. code-block:: none + :copyable: false + + Error: Call to a member function update() on null + +.. _laravel-modify-documents-upsert: + +Update or Insert in a Single Operation +-------------------------------------- + +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. + +To specify an upsert in an ``update()`` method, set the ``upsert`` option to +``true`` as shown in the following code example: + +.. code-block:: php + :emphasize-lines: 4 + :copyable: false + + YourModel::where(/* match criteria */) + ->update( + [/* update data */], + ['upsert' => true]); + +When the ``update()`` method is chained to a query, it performs one of the +following actions: + +- If the query matches documents, the ``update()`` method modifies the matching + documents. +- 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 +~~~~~~~~~~~~~~ + +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 +:guilabel:`VIEW OUTPUT` button to see the example document inserted when no +matching documents exist: + +.. 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": "660c...", + "performer": "Jon Batiste", + "venue": "Radio City Music Hall", + "genres": [ + "R&B", + "soul" + ], + "ticketsSold": 4000, + "updated_at": ... + } + +.. _laravel-modify-documents-arrays: + +Update Arrays in a Document +--------------------------- + +In this section, you can see examples of the following operations that +update array values in a MongoDB document: + +- :ref:`Add values to an array ` +- :ref:`Remove values from an array ` +- :ref:`Update the value of an array element ` + +These examples modify the sample document created by the following insert +operation: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin array example document + :end-before: end array example document + +.. _laravel-modify-documents-add-array-values: + +Add Values to an Array Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section shows how to use the ``push()`` method to add values to an array +in a MongoDB document. You can pass one or more values to add and set the +optional parameter ``unique`` to ``true`` to skip adding any duplicate values +in the array. The following code example shows the structure of a ``push()`` +method call: + +.. code-block:: none + :copyable: false + + YourModel::where() + ->push( + , + [], // array or single value to add + unique: true); // whether to skip existing values + +The following example shows how to add the value ``"baroque"`` to +the ``genres`` array field of a matching document. Click the +:guilabel:`VIEW OUTPUT` button to see the updated document: + +.. io-code-block:: + + .. input:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model array push + :end-before: end model array push + + .. output:: + :language: json + :visible: false + + { + "_id": "660eb...", + "performer": "Mitsuko Uchida", + "genres": [ + "classical", + "dance-pop", + + ], + "updated_at": ..., + "created_at": ... + } + + +.. _laravel-modify-documents-remove-array-values: + +Remove Values From an Array Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section shows how to use the ``pull()`` method to remove values from +an array in a MongoDB document. You can pass one or more values to remove +from the array. The following code example shows the structure of a +``pull()`` method call: + +.. code-block:: none + :copyable: false + + YourModel::where() + ->pull( + , + []); // array or single value to remove + +The following example shows how to remove array values ``"classical"`` and +``"dance-pop"`` from the ``genres`` array field. Click the +:guilabel:`VIEW OUTPUT` button to see the updated document: + +.. io-code-block:: + + .. input:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model array pull + :end-before: end model array pull + + .. output:: + :language: json + :visible: false + + { + "_id": "660e...", + "performer": "Mitsuko Uchida", + "genres": [], + "updated_at": ..., + "created_at": ... + } + + +.. _laravel-modify-documents-update-array-values: + +Update the Value of an Array Element Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section shows how to use the ``$`` positional operator to update specific +array elements in a MongoDB document. The ``$`` operator represents the first +array element that matches the query. The following code example shows the +structure of a positional operator update call on a single matching document: + + +.. note:: + + Currently, {+odm-short+} offers this operation only on the ``DB`` facade + and not on the Eloquent ORM. + +.. code-block:: none + :copyable: false + + DB::connection('mongodb') + ->getCollection() + ->updateOne( + , + ['$set' => ['.$' => ]]); + + +The following example shows how to replace the array value ``"dance-pop"`` +with ``"contemporary"`` in the ``genres`` array field. Click the +:guilabel:`VIEW OUTPUT` button to see the updated document: + +.. io-code-block:: + + .. input:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model array positional + :end-before: end model array positional + + .. output:: + :language: json + :visible: false + + { + "_id": "660e...", + "performer": "Mitsuko Uchida", + "genres": [ + "classical", + "contemporary" + ], + "updated_at": ..., + "created_at": ... + } + +To learn more about array update operators, see :manual:`Array Update Operators ` +in the {+server-docs-name+}. + +.. _laravel-fundamentals-delete-documents: + +Delete Documents +---------------- + +In this section, you can learn how to delete documents from a MongoDB collection +by using {+odm-short+}. Use delete operations to remove data from your MongoDB +database. + +This section provides examples of the following delete operations: + +- :ref:`Delete one document ` +- :ref:`Delete multiple documents ` + +To learn about the Laravel features available in {+odm-short+} that modify +delete behavior, see the following sections: + +- :ref:`Soft delete `, which lets you mark + documents as deleted instead of removing them from the database +- :ref:`Pruning `, which lets you define conditions + that qualify a document for automatic deletion + +.. _laravel-fundamentals-delete-one: + +Delete a Document Examples +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete one document in the following ways: + +- Call the ``$model->delete()`` method on an instance of the model. +- Call the ``Model::destroy($id)`` method on the model, passing it the id of + the document to be deleted. +- Chain methods to retrieve and delete an instance of a model by calling the + ``delete()`` method. + +The following example shows how to delete a document by calling ``$model->delete()`` +on an instance of the model: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin delete one model + :end-before: end delete one model + :caption: Delete the document by calling the delete() method on an instance. + +When the ``delete()`` method succeeds, the operation returns the number of +documents deleted. + +If the retrieve part of the call does not match any documents in the collection, +the operation returns ``0``. + +The following example shows how to delete a document by passing the value of +its id to the ``Model::destroy($id)`` method: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model delete by id + :end-before: end model delete by id + :caption: Delete the document by its id value. + +When the ``destroy()`` method succeeds, it returns the number of documents +deleted. + +If the id value does not match any documents, the ``destroy()`` method +returns returns ``0``. + +The following example shows how to chain calls to retrieve the first +matching document and delete it: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model delete one fluent + :end-before: end model delete one fluent + :caption: Delete the matching document by chaining the delete() method. + +.. include:: /includes/fact-orderby-id.rst + +When the ``delete()`` method succeeds, it returns the number of documents +deleted. + +If the ``where()`` method does not match any documents, the ``delete()`` method +returns returns ``0``. + +.. _laravel-fundamentals-delete-many: + +Delete Multiple Documents Examples +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete multiple documents in the following ways: + + +- Call the ``Model::destroy($ids)`` method, passing a list of the ids of the + documents or model instances to be deleted. +- Chain methods to retrieve a Laravel collection object that references + multiple objects and delete them by calling the ``delete()`` method. + +The following example shows how to delete a document by passing an array of +id values, represented by ``$ids``, to the ``destroy()`` method: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model delete multiple by id + :end-before: end model delete multiple by id + :caption: Delete documents by their ids. + +.. tip:: + + The ``destroy()`` method performance suffers when passed large lists. For + better performance, use ``Model::whereIn('id', $ids)->delete()`` instead. + +When the ``destroy()`` method succeeds, it returns the number of documents +deleted. + +If the id values do not match any documents, the ``destroy()`` method +returns returns ``0``. + +The following example shows how to chain calls to retrieve matching documents +and delete them: + +.. literalinclude:: /includes/fundamentals/write-operations/WriteOperationsTest.php + :language: php + :dedent: + :start-after: begin model delete multiple fluent + :end-before: end model delete multiple fluent + :caption: Chain calls to retrieve matching documents and delete them. + +When the ``delete()`` method succeeds, it returns the number of documents +deleted. + +If the ``where()`` method does not match any documents, the ``delete()`` method +returns ``0``. + diff --git a/docs/includes/fact-orderby-id.rst b/docs/includes/fact-orderby-id.rst new file mode 100644 index 000000000..c2f9981e0 --- /dev/null +++ b/docs/includes/fact-orderby-id.rst @@ -0,0 +1,6 @@ +.. 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 ` + glossary entry in the {+server-docs-name+}. diff --git a/docs/includes/fundamentals/write-operations/Concert.php b/docs/includes/fundamentals/write-operations/Concert.php new file mode 100644 index 000000000..69b36b9a5 --- /dev/null +++ b/docs/includes/fundamentals/write-operations/Concert.php @@ -0,0 +1,12 @@ + 'datetime']; +} diff --git a/docs/includes/fundamentals/write-operations/WriteOperationsTest.php b/docs/includes/fundamentals/write-operations/WriteOperationsTest.php new file mode 100644 index 000000000..d577ef57b --- /dev/null +++ b/docs/includes/fundamentals/write-operations/WriteOperationsTest.php @@ -0,0 +1,518 @@ +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 + + // begin inserted id + $insertedId = $concert->id; + // end inserted id + + $this->assertNotNull($concert); + $this->assertNotNull($insertedId); + + $result = Concert::first(); + $this->assertInstanceOf(Concert::class, $result); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelInsertMassAssign(): void + { + require_once __DIR__ . '/Concert.php'; + + Concert::truncate(); + + // begin model insert one mass assign + $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 + + $this->assertNotNull($insertResult); + + $result = Concert::first(); + $this->assertInstanceOf(Concert::class, $result); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelInsertMany(): void + { + require_once __DIR__ . '/Concert.php'; + + Concert::truncate(); + + // begin model insert many + $data = [ + [ + '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')), + ], + ]; + + Concert::insert($data); + // end model insert many + + $results = Concert::get(); + + $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); + } + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelUpsert(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + // begin model upsert + Concert::where(['performer' => 'Jon Batiste', 'venue' => 'Radio City Music Hall']) + ->update( + ['genres' => ['R&B', 'soul'], 'ticketsSold' => 4000], + ['upsert' => true], + ); + // end model upsert + + $result = Concert::first(); + + $this->assertInstanceOf(Concert::class, $result); + $this->assertEquals('Jon Batiste', $result->performer); + $this->assertEquals(4000, $result->ticketsSold); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelPushArray(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + // begin array example document + Concert::create([ + 'performer' => 'Mitsuko Uchida', + 'genres' => ['classical', 'dance-pop'], + ]); + // end array example document + + // begin model array push + Concert::where('performer', 'Mitsuko Uchida') + ->push( + 'genres', + ['baroque'], + ); + // end model array push + + $result = Concert::first(); + + $this->assertInstanceOf(Concert::class, $result); + $this->assertContains('baroque', $result->genres); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelPullArray(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + Concert::create([ + 'performer' => 'Mitsuko Uchida', + 'genres' => [ 'classical', 'dance-pop' ], + ]); + + // begin model array pull + Concert::where('performer', 'Mitsuko Uchida') + ->pull( + 'genres', + ['dance-pop', 'classical'], + ); + // end model array pull + + $result = Concert::first(); + + $this->assertInstanceOf(Concert::class, $result); + $this->assertEmpty($result->genres); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testModelPositional(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + Concert::create([ + 'performer' => 'Mitsuko Uchida', + 'genres' => [ 'classical', 'dance-pop' ], + ]); + + // begin model array positional + $match = ['performer' => 'Mitsuko Uchida', 'genres' => 'dance-pop']; + $update = ['$set' => ['genres.$' => 'contemporary']]; + + DB::connection('mongodb') + ->getCollection('concerts') + ->updateOne($match, $update); + // end model array positional + + $result = Concert::first(); + + $this->assertInstanceOf(Concert::class, $result); + $this->assertContains('classical', $result->genres); + $this->assertContains('contemporary', $result->genres); + $this->assertFalse(in_array('dance-pop', $result->genres)); + } + + public function testModelDeleteById(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + $data = [ + [ + '_id' => 'CH-0401242000', + 'performer' => 'Mitsuko Uchida', + 'venue' => 'Carnegie Hall', + 'genres' => ['classical'], + 'ticketsSold' => 2121, + 'performanceDate' => new UTCDateTime(Carbon::create(2024, 4, 1, 20, 0, 0, 'EST')), + ], + [ + '_id' => 'MSG-0212252000', + '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')), + ], + [ + '_id' => 'MSG-021222000', + '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')), + ], + [ + '_id' => 'SF-06302000', + 'performer' => 'The Rolling Stones', + 'venue' => 'Soldier Field', + 'genres' => [ 'rock', 'pop', 'blues' ], + 'ticketsSold' => 59527, + 'performanceDate' => new UTCDateTime(Carbon::create(2024, 6, 30, 20, 0, 0, 'CDT')), + ], + ]; + Concert::insert($data); + + $this->assertEquals(4, Concert::count()); + + $id = Concert::first()->id; + + // begin model delete by id + $id = 'MSG-0212252000'; + Concert::destroy($id); + // end model delete by id + + $this->assertEquals(3, Concert::count()); + $this->assertNull(Concert::find($id)); + } + + public function testModelDeleteModel(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + $data = [ + [ + 'performer' => 'Mitsuko Uchida', + 'venue' => 'Carnegie Hall', + ], + ]; + Concert::insert($data); + + // begin delete one model + $concert = Concert::first(); + $concert->delete(); + // end delete one model + + $this->assertEquals(0, Concert::count()); + } + + public function testModelDeleteFirst(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + $data = [ + [ + 'performer' => 'Mitsuko Uchida', + 'venue' => 'Carnegie Hall', + ], + [ + 'performer' => 'Brad Mehldau', + 'venue' => 'Philharmonie de Paris', + ], + [ + 'performer' => 'Billy Joel', + 'venue' => 'Madison Square Garden', + ], + [ + 'performer' => 'The Rolling Stones', + 'venue' => 'Soldier Field', + ], + ]; + Concert::insert($data); + + // begin model delete one fluent + Concert::where('venue', 'Carnegie Hall') + ->limit(1) + ->delete(); + // end model delete one fluent + + $this->assertEquals(3, Concert::count()); + } + + public function testModelDeleteMultipleById(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + $data = [ + [ + '_id' => 3, + 'performer' => 'Mitsuko Uchida', + 'venue' => 'Carnegie Hall', + ], + [ + '_id' => 5, + 'performer' => 'Brad Mehldau', + 'venue' => 'Philharmonie de Paris', + ], + [ + '_id' => 7, + 'performer' => 'Billy Joel', + 'venue' => 'Madison Square Garden', + ], + [ + '_id' => 9, + 'performer' => 'The Rolling Stones', + 'venue' => 'Soldier Field', + ], + ]; + Concert::insert($data); + + // begin model delete multiple by id + $ids = [3, 5, 7, 9]; + Concert::destroy($ids); + // end model delete multiple by id + + $this->assertEquals(0, Concert::count()); + } + + public function testModelDeleteMultiple(): void + { + require_once __DIR__ . '/Concert.php'; + Concert::truncate(); + + $data = [ + [ + 'performer' => 'Mitsuko Uchida', + 'venue' => 'Carnegie Hall', + 'genres' => ['classical'], + 'ticketsSold' => 2121, + ], + [ + 'performer' => 'Brad Mehldau', + 'venue' => 'Philharmonie de Paris', + 'genres' => [ 'jazz', 'post-bop' ], + 'ticketsSold' => 5745, + ], + [ + 'performer' => 'Billy Joel', + 'venue' => 'Madison Square Garden', + 'genres' => [ 'rock', 'soft rock', 'pop rock' ], + 'ticketsSold' => 12852, + ], + [ + 'performer' => 'The Rolling Stones', + 'venue' => 'Soldier Field', + 'genres' => [ 'rock', 'pop', 'blues' ], + 'ticketsSold' => 59527, + ], + ]; + Concert::insert($data); + + // begin model delete multiple fluent + Concert::where('ticketsSold', '>', 7500) + ->delete(); + // end model delete multiple fluent + + $this->assertEquals(2, Concert::count()); + } +} diff --git a/docs/index.txt b/docs/index.txt index af09ee013..9f6b76483 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -16,7 +16,7 @@ Laravel MongoDB /quick-start /usage-examples Release Notes - /retrieve + /fundamentals /eloquent-models /query-builder /user-authentication @@ -61,7 +61,8 @@ Fundamentals To learn how to perform the following tasks by using {+odm-short+}, see the following content: -- :ref:`laravel-fundamentals-retrieve` +- :ref:`laravel-fundamentals-read-ops` +- :ref:`laravel-fundamentals-write-ops` - :ref:`laravel-eloquent-models` - :ref:`laravel-query-builder` - :ref:`laravel-user-authentication`