From d4a981880e0ded6f3d9ceb608372fb379813364c Mon Sep 17 00:00:00 2001 From: Chris Cho Date: Mon, 25 Mar 2024 16:56:02 -0400 Subject: [PATCH 01/14] DOCSP-37057 eloquent schema builder (#2776) * DOCSP-37057: Eloquent schema builder --- docs/eloquent-models.txt | 518 +----------------- docs/eloquent-models/schema-builder.txt | 393 +++++++++++++ .../schema-builder/astronauts_migration.php | 30 + .../schema-builder/flights_migration.php | 32 ++ .../schema-builder/passengers_migration.php | 32 ++ .../schema-builder/planets_migration.php | 33 ++ .../schema-builder/spaceports_migration.php | 33 ++ .../schema-builder/stars_migration.php | 27 + 8 files changed, 592 insertions(+), 506 deletions(-) create mode 100644 docs/eloquent-models/schema-builder.txt create mode 100644 docs/includes/schema-builder/astronauts_migration.php create mode 100644 docs/includes/schema-builder/flights_migration.php create mode 100644 docs/includes/schema-builder/passengers_migration.php create mode 100644 docs/includes/schema-builder/planets_migration.php create mode 100644 docs/includes/schema-builder/spaceports_migration.php create mode 100644 docs/includes/schema-builder/stars_migration.php diff --git a/docs/eloquent-models.txt b/docs/eloquent-models.txt index 3ce32c124..c0f7cea57 100644 --- a/docs/eloquent-models.txt +++ b/docs/eloquent-models.txt @@ -6,517 +6,23 @@ Eloquent Models .. facet:: :name: genre - :values: tutorial + :values: reference .. meta:: - :keywords: php framework, odm, code example + :keywords: php framework, odm -This package includes a MongoDB enabled Eloquent class that you can use to -define models for corresponding collections. +Eloquent models are part of the Laravel Eloquent object-relational +mapping (ORM) framework that enable you to work with a database by using +model classes. {+odm-short+} extends this framework to use similar +syntax to work with MongoDB as a database. -Extending the base model -~~~~~~~~~~~~~~~~~~~~~~~~ +This section contains guidance on how to use Eloquent models in +{+odm-short+} to work with MongoDB in the following ways: -To get started, create a new model class in your ``app\Models\`` directory. +- :ref:`laravel-schema-builder` shows how to manage indexes on your MongoDB + collections by using Laravel migrations -.. code-block:: php +.. toctree:: - namespace App\Models; + Schema Builder - use MongoDB\Laravel\Eloquent\Model; - - class Book extends Model - { - // - } - -Just like a regular model, the MongoDB model class will know which collection -to use based on the model name. For ``Book``, the collection ``books`` will -be used. - -To change the collection, pass the ``$collection`` property: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Book extends Model - { - protected $collection = 'my_books_collection'; - } - -.. note:: - - MongoDB documents are automatically stored with a unique ID that is stored - in the ``_id`` property. If you wish to use your own ID, substitute the - ``$primaryKey`` property and set it to your own primary key attribute name. - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Book extends Model - { - protected $primaryKey = 'id'; - } - - // MongoDB will also create _id, but the 'id' property will be used for primary key actions like find(). - Book::create(['id' => 1, 'title' => 'The Fault in Our Stars']); - -Likewise, you may define a ``connection`` property to override the name of the -database connection to reference the model. - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Book extends Model - { - protected $connection = 'mongodb'; - } - -Soft Deletes -~~~~~~~~~~~~ - -When soft deleting a model, it is not actually removed from your database. -Instead, a ``deleted_at`` timestamp is set on the record. - -To enable soft delete for a model, apply the ``MongoDB\Laravel\Eloquent\SoftDeletes`` -Trait to the model: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\SoftDeletes; - - class User extends Model - { - use SoftDeletes; - } - -For more information check `Laravel Docs about Soft Deleting `__. - -Prunable -~~~~~~~~ - -``Prunable`` and ``MassPrunable`` traits are Laravel features to automatically -remove models from your database. You can use ``Illuminate\Database\Eloquent\Prunable`` -trait to remove models one by one. If you want to remove models in bulk, you -must use the ``MongoDB\Laravel\Eloquent\MassPrunable`` trait instead: it -will be more performant but can break links with other documents as it does -not load the models. - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - use MongoDB\Laravel\Eloquent\MassPrunable; - - class Book extends Model - { - use MassPrunable; - } - -For more information check `Laravel Docs about Pruning Models `__. - -Dates -~~~~~ - -Eloquent allows you to work with Carbon or DateTime objects instead of MongoDate objects. Internally, these dates will be converted to MongoDate objects when saved to the database. - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - protected $casts = ['birthday' => 'datetime']; - } - -This allows you to execute queries like this: - -.. code-block:: php - - $users = User::where( - 'birthday', '>', - new DateTime('-18 years') - )->get(); - -Extending the Authenticatable base model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This package includes a MongoDB Authenticatable Eloquent class ``MongoDB\Laravel\Auth\User`` -that you can use to replace the default Authenticatable class ``Illuminate\Foundation\Auth\User`` -for your ``User`` model. - -.. code-block:: php - - use MongoDB\Laravel\Auth\User as Authenticatable; - - class User extends Authenticatable - { - - } - -Guarding attributes -~~~~~~~~~~~~~~~~~~~ - -When choosing between guarding attributes or marking some as fillable, Taylor -Otwell prefers the fillable route. This is in light of -`recent security issues described here `__. - -Keep in mind guarding still works, but you may experience unexpected behavior. - -Schema ------- - -The database driver also has (limited) schema builder support. You can -conveniently manipulate collections and set indexes. - -Basic Usage -~~~~~~~~~~~ - -.. code-block:: php - - Schema::create('users', function ($collection) { - $collection->index('name'); - $collection->unique('email'); - }); - -You can also pass all the parameters specified :manual:`in the MongoDB docs ` -to the ``$options`` parameter: - -.. code-block:: php - - Schema::create('users', function ($collection) { - $collection->index( - 'username', - null, - null, - [ - 'sparse' => true, - 'unique' => true, - 'background' => true, - ] - ); - }); - -Inherited operations: - - -* create and drop -* collection -* hasCollection -* index and dropIndex (compound indexes supported as well) -* unique - -MongoDB specific operations: - - -* background -* sparse -* expire -* geospatial - -All other (unsupported) operations are implemented as dummy pass-through -methods because MongoDB does not use a predefined schema. - -Read more about the schema builder on `Laravel Docs `__ - -Geospatial indexes -~~~~~~~~~~~~~~~~~~ - -Geospatial indexes can improve query performance of location-based documents. - -They come in two forms: ``2d`` and ``2dsphere``. Use the schema builder to add -these to a collection. - -.. code-block:: php - - Schema::create('bars', function ($collection) { - $collection->geospatial('location', '2d'); - }); - -To add a ``2dsphere`` index: - -.. code-block:: php - - Schema::create('bars', function ($collection) { - $collection->geospatial('location', '2dsphere'); - }); - -Relationships -------------- - -Basic Usage -~~~~~~~~~~~ - -The only available relationships are: - - -* hasOne -* hasMany -* belongsTo -* belongsToMany - -The MongoDB-specific relationships are: - - -* embedsOne -* embedsMany - -Here is a small example: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - public function items() - { - return $this->hasMany(Item::class); - } - } - -The inverse relation of ``hasMany`` is ``belongsTo``: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Item extends Model - { - public function user() - { - return $this->belongsTo(User::class); - } - } - -belongsToMany and pivots -~~~~~~~~~~~~~~~~~~~~~~~~ - -The belongsToMany relation will not use a pivot "table" but will push id's to -a **related_ids** attribute instead. This makes the second parameter for the -belongsToMany method useless. - -If you want to define custom keys for your relation, set it to ``null``: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - public function groups() - { - return $this->belongsToMany( - Group::class, null, 'user_ids', 'group_ids' - ); - } - } - -EmbedsMany Relationship -~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to embed models, rather than referencing them, you can use the -``embedsMany`` relation. This relation is similar to the ``hasMany`` relation -but embeds the models inside the parent object. - -**REMEMBER**\ : These relations return Eloquent collections, they don't return -query builder objects! - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - public function books() - { - return $this->embedsMany(Book::class); - } - } - -You can access the embedded models through the dynamic property: - -.. code-block:: php - - $user = User::first(); - - foreach ($user->books as $book) { - // - } - -The inverse relation is auto *magically* available. You can omit the reverse -relation definition. - -.. code-block:: php - - $book = User::first()->books()->first(); - - $user = $book->user; - -Inserting and updating embedded models works similar to the ``hasMany`` relation: - -.. code-block:: php - - $book = $user->books()->save( - new Book(['title' => 'A Game of Thrones']) - ); - - // or - $book = - $user->books() - ->create(['title' => 'A Game of Thrones']); - -You can update embedded models using their ``save`` method (available since -release 2.0.0): - -.. code-block:: php - - $book = $user->books()->first(); - - $book->title = 'A Game of Thrones'; - $book->save(); - -You can remove an embedded model by using the ``destroy`` method on the -relation, or the ``delete`` method on the model (available since release 2.0.0): - -.. code-block:: php - - $book->delete(); - - // Similar operation - $user->books()->destroy($book); - -If you want to add or remove an embedded model, without touching the database, -you can use the ``associate`` and ``dissociate`` methods. - -To eventually write the changes to the database, save the parent object: - -.. code-block:: php - - $user->books()->associate($book); - $user->save(); - -Like other relations, embedsMany assumes the local key of the relationship -based on the model name. You can override the default local key by passing a -second argument to the embedsMany method: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - public function books() - { - return $this->embedsMany(Book::class, 'local_key'); - } - } - -Embedded relations will return a Collection of embedded items instead of a -query builder. Check out the available operations here: -`https://laravel.com/docs/master/collections `__ - -EmbedsOne Relationship -~~~~~~~~~~~~~~~~~~~~~~ - -The embedsOne relation is similar to the embedsMany relation, but only embeds a single model. - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Book extends Model - { - public function author() - { - return $this->embedsOne(Author::class); - } - } - -You can access the embedded models through the dynamic property: - -.. code-block:: php - - $book = Book::first(); - $author = $book->author; - -Inserting and updating embedded models works similar to the ``hasOne`` relation: - -.. code-block:: php - - $author = $book->author()->save( - new Author(['name' => 'John Doe']) - ); - - // Similar - $author = - $book->author() - ->create(['name' => 'John Doe']); - -You can update the embedded model using the ``save`` method (available since -release 2.0.0): - -.. code-block:: php - - $author = $book->author; - - $author->name = 'Jane Doe'; - $author->save(); - -You can replace the embedded model with a new model like this: - -.. code-block:: php - - $newAuthor = new Author(['name' => 'Jane Doe']); - - $book->author()->save($newAuthor); - -Cross-Database Relationships ----------------------------- - -If you're using a hybrid MongoDB and SQL setup, you can define relationships -across them. - -The model will automatically return a MongoDB-related or SQL-related relation -based on the type of the related model. - -If you want this functionality to work both ways, your SQL-models will need -to use the ``MongoDB\Laravel\Eloquent\HybridRelations`` trait. - -**This functionality only works for ``hasOne``, ``hasMany`` and ``belongsTo``.** - -The SQL model must use the ``HybridRelations`` trait: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\HybridRelations; - - class User extends Model - { - use HybridRelations; - - protected $connection = 'mysql'; - - public function messages() - { - return $this->hasMany(Message::class); - } - } - -Within your MongoDB model, you must define the following relationship: - -.. code-block:: php - - use MongoDB\Laravel\Eloquent\Model; - - class Message extends Model - { - protected $connection = 'mongodb'; - - public function user() - { - return $this->belongsTo(User::class); - } - } diff --git a/docs/eloquent-models/schema-builder.txt b/docs/eloquent-models/schema-builder.txt new file mode 100644 index 000000000..9fd845b55 --- /dev/null +++ b/docs/eloquent-models/schema-builder.txt @@ -0,0 +1,393 @@ +.. _laravel-schema-builder: + +============== +Schema Builder +============== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: php framework, odm, code example, schema facade, eloquent, blueprint, artisan, migrate + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +Laravel provides a **facade** to access the schema builder class ``Schema``, +which lets you create and modify tables. Facades are static interfaces to +classes that make the syntax more concise and improve testability. + +{+odm-short+} supports a subset of the index and collection management methods +in the Laravel ``Schema`` facade. + +To learn more about facades, see `Facades `__ +in the Laravel documentation. + +The following sections describe the Laravel schema builder features available +in {+odm-short+} and show examples of how to use them: + +- :ref:`` +- :ref:`` +- :ref:`` + +.. note:: + + {+odm-short+} supports managing indexes and collections, but + excludes support for MongoDB JSON schemas for data validation. To learn + more about JSON schema validation, see :manual:`Schema Validation ` + in the {+server-docs-name+}. + +.. _laravel-eloquent-migrations: + +Perform Laravel Migrations +-------------------------- + +Laravel migrations let you programmatically create, modify, and delete +your database schema by running methods included in the ``Schema`` facade. +The following sections explain how to author a migration class when you use +a MongoDB database and how to run them. + +Create a Migration Class +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create migration classes manually or generate them by using the +``php artisan make:migration`` command. If you generate them, you must make the +following changes to perform the schema changes on your MongoDB database: + +- Replace the ``Illuminate\Database\Schema\Blueprint`` import with + ``MongoDB\Laravel\Schema\Blueprint`` if it is referenced in your migration +- Use only commands and syntax supported by {+odm-short+} + +.. tip:: + + If your default database connection is set to anything other than your + MongoDB database, update the following setting to make sure the migration + specifies the correct database: + + - Specify ``mongodb`` in the ``$connection`` field of your migration class + - Set ``DB_CONNECTION=mongodb`` in your ``.env`` configuration file + +The following example migration class contains the following methods: + +- ``up()``, which creates a collection and an index when you run the migration +- ``down()``, which drops the collection and all the indexes on it when you roll back the migration + +.. literalinclude:: /includes/schema-builder/astronauts_migration.php + :dedent: + :language: php + :emphasize-lines: 6, 11 + +Run or Roll Back a Migration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To run the database migration from a class file, run the following command +after replacing the placeholder: + +.. code-block:: bash + + php artisan migrate --path= + +This command runs the ``up()`` function in the class file to create the +collection and index in the database specified in the ``config/database.php`` +file. + +To roll back the migration, run the following command after replacing the +placeholder: + +.. code-block:: bash + + php artisan migrate:rollback --path= + +This command runs the ``down()`` function in the class file to drop the +collection and related indexes. + +To learn more about Laravel migrations, see +`Database: Migrations `__ +in the Laravel documentation. + +.. _laravel-eloquent-collection-exists: + +Check Whether a Collection Exists +--------------------------------- + +To check whether a collection exists, call the ``hasCollection()`` method on +the ``Schema`` facade in your migration file. You can use this to +perform migration logic conditionally. + +The following example migration creates a ``stars`` collection if a collection +named ``telescopes`` exists: + +.. literalinclude:: /includes/schema-builder/stars_migration.php + :language: php + :dedent: + :start-after: begin conditional create + :end-before: end conditional create + +.. _laravel-eloquent-indexes: + +Manage Indexes +-------------- + +MongoDB indexes are data structures that improve query efficiency by reducing +the number of documents needed to retrieve query results. Certain indexes, such +as geospatial indexes, extend how you can query the data. + +To improve query performance by using an index, make sure the index covers +the query. To learn more about indexes and query optimization, see the +following {+server-docs-name+} entries: + +- :manual:`Indexes ` +- :manual:`Query Optimization ` + +The following sections show how you can use the schema builder to create and +drop various types of indexes on a collection. + +Create an Index +~~~~~~~~~~~~~~~ + +To create indexes, call the ``create()`` method on the ``Schema`` facade +in your migration file. Pass it the collection name and a callback +method with a ``MongoDB\Laravel\Schema\Blueprint`` parameter. Specify the +index creation details on the ``Blueprint`` instance. + +The following example migration creates indexes on the following collection +fields: + +- Single field index on ``mission_type`` +- Compound index on ``launch_location`` and ``launch_date``, specifying a descending sort order on ``launch_date`` +- Unique index on the ``mission_id`` field, specifying the index name "unique_mission_id_idx" + +Click the :guilabel:`VIEW OUTPUT` button to see the indexes created by running +the migration, including the default index on the ``_id`` field: + +.. io-code-block:: + + .. input:: /includes/schema-builder/flights_migration.php + :language: php + :dedent: + :start-after: begin create index + :end-before: end create index + + .. output:: + :language: json + :visible: false + + [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { v: 2, key: { mission_type: 1 }, name: 'mission_type_1' }, + { + v: 2, + key: { launch_location: 1, launch_date: -1 }, + name: 'launch_location_1_launch_date_-1' + }, + { + v: 2, + key: { mission_id: 1 }, + name: 'unique_mission_id_idx', + unique: true + } + ] + +Specify Index Options +~~~~~~~~~~~~~~~~~~~~~ + +MongoDB index options determine how the indexes are used and stored. +You can specify index options when calling an index creation method, such +as ``index()``, on a ``Blueprint`` instance. + +The following migration code shows how to add a collation to an index as an +index option. Click the :guilabel:`VIEW OUTPUT` button to see the indexes +created by running the migration, including the default index on the ``_id`` +field: + +.. io-code-block:: + + .. input:: /includes/schema-builder/passengers_migration.php + :language: php + :dedent: + :start-after: begin index options + :end-before: end index options + + .. output:: + :language: json + :visible: false + + [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { + v: 2, + key: { last_name: 1 }, + name: 'passengers_collation_idx', + collation: { + locale: 'de@collation=phonebook', + caseLevel: false, + caseFirst: 'off', + strength: 3, + numericOrdering: true, + alternate: 'non-ignorable', + maxVariable: 'punct', + normalization: false, + backwards: false, + version: '57.1' + } + } + ] + +To learn more about index options, see :manual:`Options for All Index Types ` +in the {+server-docs-name+}. + +Create Sparse, TTL, and Unique Indexes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use {+odm-short+} helper methods to create the following types of +indexes: + +- Sparse indexes, which allow index entries only for documents that contain the + specified field +- Time-to-live (TTL) indexes, which expire after a set amount of time +- Unique indexes, which prevent inserting documents that contain duplicate + values for the indexed field + +To create these index types, call the ``create()`` method on the ``Schema`` facade +in your migration file. Pass ``create()`` the collection name and a callback +method with a ``MongoDB\Laravel\Schema\Blueprint`` parameter. Call the +appropriate helper method on the ``Blueprint`` instance and pass the +index creation details. + +The following migration code shows how to create a sparse and a TTL index +by using the index helpers. Click the :guilabel:`VIEW OUTPUT` button to see +the indexes created by running the migration, including the default index on +the ``_id`` field: + +.. io-code-block:: + + .. input:: /includes/schema-builder/planets_migration.php + :language: php + :dedent: + :start-after: begin index helpers + :end-before: end index helpers + + .. output:: + :language: json + :visible: false + + [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { v: 2, key: { rings: 1 }, name: 'rings_1', sparse: true }, + { + v: 2, + key: { last_visible_dt: 1 }, + name: 'last_visible_dt_1', + expireAfterSeconds: 86400 + } + ] + +You can specify sparse, TTL, and unique indexes on either a single field or +compound index by specifying them in the index options. + +The following migration code shows how to create all three types of indexes +on a single field. Click the :guilabel:`VIEW OUTPUT` button to see the indexes +created by running the migration, including the default index on the ``_id`` +field: + +.. io-code-block:: + + .. input:: /includes/schema-builder/planets_migration.php + :language: php + :dedent: + :start-after: begin multi index helpers + :end-before: end multi index helpers + + .. output:: + :language: json + :visible: false + + [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { + v: 2, + key: { last_visible_dt: 1 }, + name: 'last_visible_dt_1', + unique: true, + sparse: true, + expireAfterSeconds: 3600 + } + ] + +To learn more about these indexes, see :manual:`Index Properties ` +in the {+server-docs-name+}. + +Create a Geospatial Index +~~~~~~~~~~~~~~~~~~~~~~~~~ + +In MongoDB, geospatial indexes let you query geospatial coordinate data for +inclusion, intersection, and proximity. + +To create geospatial indexes, call the ``create()`` method on the ``Schema`` facade +in your migration file. Pass ``create()`` the collection name and a callback +method with a ``MongoDB\Laravel\Schema\Blueprint`` parameter. Specify the +geospatial index creation details on the ``Blueprint`` instance. + +The following example migration creates a ``2d`` and ``2dsphere`` geospatial +index on the ``spaceports`` collection. Click the :guilabel:`VIEW OUTPUT` +button to see the indexes created by running the migration, including the +default index on the ``_id`` field: + +.. io-code-block:: + .. input:: /includes/schema-builder/spaceports_migration.php + :language: php + :dedent: + :start-after: begin create geospatial index + :end-before: end create geospatial index + + .. output:: + :language: json + :visible: false + + [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { + v: 2, + key: { launchpad_location: '2dsphere' }, + name: 'launchpad_location_2dsphere', + '2dsphereIndexVersion': 3 + }, + { v: 2, key: { runway_location: '2d' }, name: 'runway_location_2d' } + ] + + +To learn more about geospatial indexes, see +:manual:`Geospatial Indexes ` in +the {+server-docs-name+}. + +Drop an Index +~~~~~~~~~~~~~ + +To drop indexes from a collection, call the ``table()`` method on the +``Schema`` facade in your migration file. Pass it the table name and a +callback method with a ``MongoDB\Laravel\Schema\Blueprint`` parameter. +Call the ``dropIndex()`` method with the index name on the ``Blueprint`` +instance. + +.. note:: + + If you drop a collection, MongoDB automatically drops all the indexes + associated with it. + +The following example migration drops an index called ``unique_mission_id_idx`` +from the ``flights`` collection: + +.. literalinclude:: /includes/schema-builder/flights_migration.php + :language: php + :dedent: + :start-after: begin drop index + :end-before: end drop index + + diff --git a/docs/includes/schema-builder/astronauts_migration.php b/docs/includes/schema-builder/astronauts_migration.php new file mode 100644 index 000000000..1fb7b76e4 --- /dev/null +++ b/docs/includes/schema-builder/astronauts_migration.php @@ -0,0 +1,30 @@ +index('name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('astronauts'); + } +}; diff --git a/docs/includes/schema-builder/flights_migration.php b/docs/includes/schema-builder/flights_migration.php new file mode 100644 index 000000000..861c339ef --- /dev/null +++ b/docs/includes/schema-builder/flights_migration.php @@ -0,0 +1,32 @@ +index('mission_type'); + $collection->index(['launch_location' => 1, 'launch_date' => -1]); + $collection->unique('mission_id', options: ['name' => 'unique_mission_id_idx']); + }); + // end create index + } + + public function down(): void + { + // begin drop index + Schema::table('flights', function (Blueprint $collection) { + $collection->dropIndex('unique_mission_id_idx'); + }); + // end drop index + } +}; diff --git a/docs/includes/schema-builder/passengers_migration.php b/docs/includes/schema-builder/passengers_migration.php new file mode 100644 index 000000000..f0b498940 --- /dev/null +++ b/docs/includes/schema-builder/passengers_migration.php @@ -0,0 +1,32 @@ +index( + 'last_name', + name: 'passengers_collation_idx', + options: [ + 'collation' => [ 'locale' => 'de@collation=phonebook', 'numericOrdering' => true ], + ], + ); + }); + // end index options + } + + public function down(): void + { + Schema::drop('passengers'); + } +}; diff --git a/docs/includes/schema-builder/planets_migration.php b/docs/includes/schema-builder/planets_migration.php new file mode 100644 index 000000000..90de5bd6e --- /dev/null +++ b/docs/includes/schema-builder/planets_migration.php @@ -0,0 +1,33 @@ +sparse('rings'); + $collection->expire('last_visible_dt', 86400); + }); + // end index helpers + + // begin multi index helpers + Schema::create('planet_systems', function (Blueprint $collection) { + $collection->index('last_visible_dt', options: ['sparse' => true, 'expireAfterSeconds' => 3600, 'unique' => true]); + }); + // end multi index helpers + } + + public function down(): void + { + Schema::drop('planets'); + } +}; diff --git a/docs/includes/schema-builder/spaceports_migration.php b/docs/includes/schema-builder/spaceports_migration.php new file mode 100644 index 000000000..ae96c6066 --- /dev/null +++ b/docs/includes/schema-builder/spaceports_migration.php @@ -0,0 +1,33 @@ +geospatial('launchpad_location', '2dsphere'); + $collection->geospatial('runway_location', '2d'); + }); + // end create geospatial index + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('spaceports'); + } +}; diff --git a/docs/includes/schema-builder/stars_migration.php b/docs/includes/schema-builder/stars_migration.php new file mode 100644 index 000000000..6249da3cd --- /dev/null +++ b/docs/includes/schema-builder/stars_migration.php @@ -0,0 +1,27 @@ + Date: Mon, 25 Mar 2024 17:01:20 -0400 Subject: [PATCH 02/14] DOCSP-35964 eloquent models standardization (#2726) * DOCSP-35964: Eloquent Models section --- docs/eloquent-models.txt | 6 +- docs/eloquent-models/model-class.txt | 317 ++++++++++++++++++ .../eloquent-models/AuthenticatableUser.php | 9 + docs/includes/eloquent-models/Planet.php | 9 + .../eloquent-models/PlanetCollection.php | 10 + docs/includes/eloquent-models/PlanetDate.php | 12 + .../eloquent-models/PlanetMassAssignment.php | 15 + .../eloquent-models/PlanetMassPrune.php | 18 + .../eloquent-models/PlanetPrimaryKey.php | 10 + docs/includes/eloquent-models/PlanetPrune.php | 22 ++ .../eloquent-models/PlanetSoftDelete.php | 11 + phpcs.xml.dist | 12 + 12 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 docs/eloquent-models/model-class.txt create mode 100644 docs/includes/eloquent-models/AuthenticatableUser.php create mode 100644 docs/includes/eloquent-models/Planet.php create mode 100644 docs/includes/eloquent-models/PlanetCollection.php create mode 100644 docs/includes/eloquent-models/PlanetDate.php create mode 100644 docs/includes/eloquent-models/PlanetMassAssignment.php create mode 100644 docs/includes/eloquent-models/PlanetMassPrune.php create mode 100644 docs/includes/eloquent-models/PlanetPrimaryKey.php create mode 100644 docs/includes/eloquent-models/PlanetPrune.php create mode 100644 docs/includes/eloquent-models/PlanetSoftDelete.php diff --git a/docs/eloquent-models.txt b/docs/eloquent-models.txt index c0f7cea57..2bca40f2d 100644 --- a/docs/eloquent-models.txt +++ b/docs/eloquent-models.txt @@ -19,10 +19,12 @@ syntax to work with MongoDB as a database. This section contains guidance on how to use Eloquent models in {+odm-short+} to work with MongoDB in the following ways: +- :ref:`laravel-eloquent-model-class` shows how to define models and customize + their behavior - :ref:`laravel-schema-builder` shows how to manage indexes on your MongoDB collections by using Laravel migrations - + .. toctree:: + /eloquent-models/model-class/ Schema Builder - diff --git a/docs/eloquent-models/model-class.txt b/docs/eloquent-models/model-class.txt new file mode 100644 index 000000000..85b7b994b --- /dev/null +++ b/docs/eloquent-models/model-class.txt @@ -0,0 +1,317 @@ +.. _laravel-eloquent-model-class: + +==================== +Eloquent Model Class +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: php framework, odm, code example, authentication, laravel + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +This guide shows you how to use the {+odm-long+} to define and +customize Laravel Eloquent models. You can use these models to work with +MongoDB data by using the Laravel Eloquent object-relational mapper (ORM). + +The following sections explain how to add Laravel Eloquent ORM behaviors +to {+odm-short+} models: + +- :ref:`laravel-model-define` demonstrates how to create a model class. +- :ref:`laravel-authenticatable-model` shows how to set MongoDB as the + authentication user provider. +- :ref:`laravel-model-customize` explains several model class customizations. +- :ref:`laravel-model-pruning` shows how to periodically remove models that + you no longer need. + +.. _laravel-model-define: + +Define an Eloquent Model Class +------------------------------ + +Eloquent models are classes that represent your data. They include methods +that perform database operations such as inserts, updates, and deletes. + +To declare a {+odm-short+} model, create a class in the ``app/Models`` +directory of your Laravel application that extends +``MongoDB\Laravel\Eloquent\Model`` as shown in the following code example: + +.. literalinclude:: /includes/eloquent-models/Planet.php + :language: php + :emphasize-lines: 3,5,7 + :dedent: + +By default, the model uses the MongoDB database name set in your Laravel +application's ``config/database.php`` setting and the snake case plural +form of your model class name for the collection. + +This model is stored in the ``planets`` MongoDB collection. + +.. tip:: + + Alternatively, use the ``artisan`` console to generate the model class and + change the ``Illuminate\Database\Eloquent\Model`` import to ``MongoDB\Laravel\Eloquent\Model``. + To learn more about the ``artisan`` console, see `Artisan Console `__ + in the Laravel docs. + +To learn how to specify the database name that your Laravel application uses, +:ref:`laravel-quick-start-connect-to-mongodb`. + + +.. _laravel-authenticatable-model: + +Extend the Authenticatable Model +-------------------------------- + +To configure MongoDB as the Laravel user provider, you can extend the +{+odm-short+} ``MongoDB\Laravel\Auth\User`` class. The following code example +shows how to extend this class: + +.. literalinclude:: /includes/eloquent-models/AuthenticatableUser.php + :language: php + :emphasize-lines: 3,5,7 + :dedent: + +To learn more about customizing a Laravel authentication user provider, +see `Adding Custom User Providers `__ +in the Laravel docs. + +.. _laravel-model-customize: + +Customize an Eloquent Model Class +--------------------------------- + +This section shows how to perform the following Eloquent model behavior +customizations: + +- :ref:`laravel-model-customize-collection-name` +- :ref:`laravel-model-customize-primary-key` +- :ref:`laravel-model-soft-delete` +- :ref:`laravel-model-cast-data-types` +- :ref:`laravel-model-mass-assignment` + +.. _laravel-model-customize-collection-name: + +Change the Model Collection Name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, the model uses the snake case plural form of your model +class name. To change the name of the collection the model uses to retrieve +and save data in MongoDB, override the ``$collection`` property of the model +class. + +.. note:: + + We recommend using the default collection naming behavior to keep + the associations between models and collections straightforward. + +The following example specifies the custom MongoDB collection name, +``celestial_body``, for the ``Planet`` class: + +.. literalinclude:: /includes/eloquent-models/PlanetCollection.php + :language: php + :emphasize-lines: 9 + :dedent: + +Without overriding the ``$collection`` property, this model maps to the +``planets`` collection. With the overridden property, the example class stores +the model in the ``celestial_body`` collection. + +.. _laravel-model-customize-primary-key: + +Change the Primary Key Field +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To customize the model's primary key field that uniquely identifies a MongoDB +document, override the ``$primaryKey`` property of the model class. + +By default, the model uses the PHP MongoDB driver to generate unique ObjectIDs +for each document your Laravel application inserts. + +The following example specifies the ``name`` field as the primary key for +the ``Planet`` class: + +.. literalinclude:: /includes/eloquent-models/PlanetPrimaryKey.php + :language: php + :emphasize-lines: 9 + :dedent: + +To learn more about primary key behavior and customization options, see +`Eloquent Primary Keys `__ +in the Laravel docs. + +To learn more about the ``_id`` field, ObjectIDs, and the MongoDB document +structure, see :manual:`Documents ` in the MongoDB server docs. + +.. _laravel-model-soft-delete: + +Enable Soft Deletes +~~~~~~~~~~~~~~~~~~~ + +Eloquent includes a soft delete feature that changes the behavior of the +``delete()`` method on a model. When soft delete is enabled on a model, the +``delete()`` method marks a document as deleted instead of removing it from the +database. It sets a timestamp on the ``deleted_at`` field to exclude it from +retrieve operations automatically. + +To enable soft deletes on a class, add the ``MongoDB\Laravel\Eloquent\SoftDeletes`` +trait as shown in the following code example: + +.. literalinclude:: /includes/eloquent-models/PlanetSoftDelete.php + :language: php + :emphasize-lines: 6,10 + :dedent: + +To learn about methods you can perform on models with soft deletes enabled, see +`Eloquent Soft Deleting `__ +in the Laravel docs. + +.. _laravel-model-cast-data-types: + +Cast Data Types +--------------- + +Eloquent lets you convert model attribute data types before storing or +retrieving data by using a casting helper. This helper is a convenient +alternative to defining equivalent accessor and mutator methods on your model. + +In the following example, the casting helper converts the ``discovery_dt`` +model attribute, stored in MongoDB as a `MongoDB\\BSON\\UTCDateTime `__ +type, to the Laravel ``datetime`` type. + +.. literalinclude:: /includes/eloquent-models/PlanetDate.php + :language: php + :emphasize-lines: 9-11 + :dedent: + +This conversion lets you use the PHP `DateTime `__ +or the `Carbon class `__ to work with dates +in this field. The following example shows a Laravel query that uses the +casting helper on the model to query for planets with a ``discovery_dt`` of +less than three years ago: + +.. code-block:: php + + Planet::where( 'discovery_dt', '>', new DateTime('-3 years'))->get(); + +To learn more about MongoDB's data types, see :manual:`BSON Types ` +in the MongoDB server docs. + +To learn more about the Laravel casting helper and supported types, see `Attribute Casting `__ +in the Laravel docs. + +.. _laravel-model-mass-assignment: + +Customize Mass Assignment +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Eloquent lets you create several models and their attribute data by passing +an array of data to the ``create()`` model method. This process of inserting +multiple models is called mass assignment. + +Mass assignment can be an efficient way to create multiple models. However, it +can expose an exploitable security vulnerability. The data in the fields +might contain updates that lead to unauthorized permissions or access. + +Eloquent provides the following traits to protect your data from mass +assignment vulnerabilities: + +- ``$fillable`` contains the fields that are writeable in a mass assignment +- ``$guarded`` contains the fields that are ignored in a mass assignment + +.. important:: + + We recommend using ``$fillable`` instead of ``$guarded`` to protect against + vulnerabilities. To learn more about this recommendation, see the + `Security Release: Laravel 6.18.35, 7.24.0 `__ + article on the Laravel site. + +In the following example, the model allows mass assignment of the fields +by using the ``$fillable`` attribute: + +.. literalinclude:: /includes/eloquent-models/PlanetMassAssignment.php + :language: php + :emphasize-lines: 9-14 + :dedent: + +The following code example shows mass assignment of the ``Planet`` model: + +.. code-block:: php + + $planets = [ + [ 'name' => 'Earth', gravity => 9.8, day_length => '24 hours' ], + [ 'name' => 'Mars', gravity => 3.7, day_length => '25 hours' ], + ]; + + Planet::create($planets); + +The models saved to the database contain only the ``name`` and ``gravity`` +fields since ``day_length`` is omitted from the ``$fillable`` attribute. + +To learn how to change the behavior when attempting to fill a field omitted +from the ``$fillable`` array, see `Mass Assignment Exceptions `__ +in the Laravel docs. + +.. _laravel-model-pruning: + +Specify Pruning Behavior +------------------------ + +Eloquent lets you specify criteria to periodically delete model data that you +no longer need. When you schedule or run the ``model:prune`` command, +Laravel calls the ``prunable()`` method on all models that import the +``Prunable`` and ``MassPrunable`` traits to match the models for deletion. + +To use this feature with models that use MongoDB as a database, add the +appropriate import to your model: + +- ``MongoDB\Laravel\Eloquent\Prunable`` optionally performs a cleanup + step before deleting a model that matches the criteria +- ``MongoDB\Laravel\Eloquent\MassPrunable`` deletes models that match the + criteria without fetching the model data + +.. note:: + + When enabling soft deletes on a mass prunable model, you must import the + following {+odm-short+} packages: + + - ``MongoDB\Laravel\Eloquent\SoftDeletes`` + - ``MongoDB\Laravel\Eloquent\MassPrunable`` + + +To learn more about the pruning feature, see `Pruning Models `__ +in the Laravel docs. + +Prunable Example +~~~~~~~~~~~~~~~~ + +The following prunable class includes a ``prunable()`` method that matches +models that the prune action deletes and a ``pruning()`` method that runs +before deleting a matching model: + +.. literalinclude:: /includes/eloquent-models/PlanetPrune.php + :language: php + :emphasize-lines: 6,10,12,18 + :dedent: + +Mass Prunable Example +~~~~~~~~~~~~~~~~~~~~~ + +The following mass prunable class includes a ``prunable()`` method that matches +models that the prune action deletes: + +.. literalinclude:: /includes/eloquent-models/PlanetMassPrune.php + :language: php + :emphasize-lines: 5,10,12 + :dedent: + diff --git a/docs/includes/eloquent-models/AuthenticatableUser.php b/docs/includes/eloquent-models/AuthenticatableUser.php new file mode 100644 index 000000000..694a595df --- /dev/null +++ b/docs/includes/eloquent-models/AuthenticatableUser.php @@ -0,0 +1,9 @@ + 'datetime', + ]; +} diff --git a/docs/includes/eloquent-models/PlanetMassAssignment.php b/docs/includes/eloquent-models/PlanetMassAssignment.php new file mode 100644 index 000000000..b2a91cab1 --- /dev/null +++ b/docs/includes/eloquent-models/PlanetMassAssignment.php @@ -0,0 +1,15 @@ +', 0.5); + } +} diff --git a/docs/includes/eloquent-models/PlanetPrimaryKey.php b/docs/includes/eloquent-models/PlanetPrimaryKey.php new file mode 100644 index 000000000..761593941 --- /dev/null +++ b/docs/includes/eloquent-models/PlanetPrimaryKey.php @@ -0,0 +1,10 @@ + + docs src tests @@ -36,5 +37,16 @@ + + + + docs/**/*.php + + + docs/**/*.php + + + docs/**/*.php + From 2117c2c73d19b0e8c14ee4854b740192d7d45e51 Mon Sep 17 00:00:00 2001 From: Chris Cho Date: Mon, 25 Mar 2024 17:07:57 -0400 Subject: [PATCH 03/14] DOCSP-37056 eloquent relationships (#2747) * DOCSP-37056: Eloquent relationships --- docs/eloquent-models.txt | 10 +- docs/eloquent-models/relationships.txt | 536 ++++++++++++++++++ .../relationships/RelationshipController.php | 194 +++++++ .../relationships/cross-db/Passenger.php | 18 + .../relationships/cross-db/SpaceShip.php | 21 + .../relationships/embeds/Cargo.php | 12 + .../relationships/embeds/SpaceShip.php | 18 + .../relationships/many-to-many/Planet.php | 18 + .../many-to-many/SpaceExplorer.php | 18 + .../relationships/one-to-many/Moon.php | 18 + .../relationships/one-to-many/Planet.php | 18 + .../relationships/one-to-one/Orbit.php | 18 + .../relationships/one-to-one/Planet.php | 18 + 13 files changed, 914 insertions(+), 3 deletions(-) create mode 100644 docs/eloquent-models/relationships.txt create mode 100644 docs/includes/eloquent-models/relationships/RelationshipController.php create mode 100644 docs/includes/eloquent-models/relationships/cross-db/Passenger.php create mode 100644 docs/includes/eloquent-models/relationships/cross-db/SpaceShip.php create mode 100644 docs/includes/eloquent-models/relationships/embeds/Cargo.php create mode 100644 docs/includes/eloquent-models/relationships/embeds/SpaceShip.php create mode 100644 docs/includes/eloquent-models/relationships/many-to-many/Planet.php create mode 100644 docs/includes/eloquent-models/relationships/many-to-many/SpaceExplorer.php create mode 100644 docs/includes/eloquent-models/relationships/one-to-many/Moon.php create mode 100644 docs/includes/eloquent-models/relationships/one-to-many/Planet.php create mode 100644 docs/includes/eloquent-models/relationships/one-to-one/Orbit.php create mode 100644 docs/includes/eloquent-models/relationships/one-to-one/Planet.php diff --git a/docs/eloquent-models.txt b/docs/eloquent-models.txt index 2bca40f2d..e7edadcfe 100644 --- a/docs/eloquent-models.txt +++ b/docs/eloquent-models.txt @@ -12,19 +12,23 @@ Eloquent Models :keywords: php framework, odm Eloquent models are part of the Laravel Eloquent object-relational -mapping (ORM) framework that enable you to work with a database by using -model classes. {+odm-short+} extends this framework to use similar -syntax to work with MongoDB as a database. +mapping (ORM) framework, which lets you to work with data in a relational +database by using model classes and Eloquent syntax. {+odm-short+} extends +this framework so that you can use Eloquent syntax to work with data in a +MongoDB database. This section contains guidance on how to use Eloquent models in {+odm-short+} to work with MongoDB in the following ways: - :ref:`laravel-eloquent-model-class` shows how to define models and customize their behavior +- :ref:`laravel-eloquent-model-relationships` shows how to define relationships + between models - :ref:`laravel-schema-builder` shows how to manage indexes on your MongoDB collections by using Laravel migrations .. toctree:: /eloquent-models/model-class/ + Relationships Schema Builder diff --git a/docs/eloquent-models/relationships.txt b/docs/eloquent-models/relationships.txt new file mode 100644 index 000000000..92625f076 --- /dev/null +++ b/docs/eloquent-models/relationships.txt @@ -0,0 +1,536 @@ +.. _laravel-eloquent-model-relationships: + +============================ +Eloquent Model Relationships +============================ + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: php framework, odm, code example, entity relationship, eloquent + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +When you use a relational database, the Eloquent ORM stores models as rows +in tables that correspond to the model classes. When you use MongoDB, the +{+odm-short+} stores models as documents in collections that correspond to the +model classes. + +To define a relationship, add a function to the model class that calls the +appropriate relationship method. This function allows you to access the related +model as a **dynamic property**. A dynamic property lets you access the +related model by using the same syntax as you use to access a property on the +model. + +The following sections describe the Laravel Eloquent and MongoDB-specific +relationships available in {+odm-short+} and show examples of how to define +and use them: + +- :ref:`One to one relationship `, + created by using the ``hasOne()`` method and its inverse, ``belongsTo()`` +- :ref:`One to many relationship `, + created by using the ``hasMany()`` and its inverse, ``belongsTo()`` +- :ref:`Many to many relationship `, + created by using the ``belongsToMany()`` method +- :ref:`Embedded document pattern `, a + MongoDB-specific relationship that can represent a one to one or one to many + relationship, created by using the ``embedsOne()`` or ``embedsMany()`` method +- :ref:`Cross-database relationships `, + required when you want to create relationships between MongoDB and SQL models + +.. _laravel-eloquent-relationship-one-to-one: + +One to One Relationship +----------------------- + +A one to one relationship between models consists of a model record related to +exactly one other type of model record. + +When you add a one to one relationship, Eloquent lets you access the model by +using a dynamic property and stores the model's document ID on the related +model. + +In {+odm-short+}, you can define a one to one relationship by using the +``hasOne()`` method or ``belongsTo()`` method. + +When you add the inverse of the relationship by using the ``belongsTo()`` +method, Eloquent lets you access the model by using a dynamic property, but +does not add any fields. + +To learn more about one to one relationships, see +`One to One `__ +in the Laravel documentation. + +One to One Example +~~~~~~~~~~~~~~~~~~ + +The following example class shows how to define a ``HasOne`` one to one +relationship between a ``Planet`` and ``Orbit`` model by using the +``hasOne()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/one-to-one/Planet.php + :language: php + :dedent: + +The following example class shows how to define the inverse ``BelongsTo`` +relationship between ``Orbit`` and ``Planet`` by using the ``belongsTo()`` +method: + +.. literalinclude:: /includes/eloquent-models/relationships/one-to-one/Orbit.php + :language: php + :dedent: + +The following sample code shows how to instantiate a model for each class +and add the relationship between them. Click the :guilabel:`VIEW OUTPUT` +button to see the data created by running the code: + +.. io-code-block:: + + .. input:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin one-to-one save + :end-before: end one-to-one save + + .. output:: + :language: json + :visible: false + + // Document in the "planets" collection + { + _id: ObjectId('65de67fb2e59d63e6d07f8b8'), + name: 'Earth', + diameter_km: 12742, + // ... + } + + // Document in the "orbits" collection + { + _id: ObjectId('65de67fb2e59d63e6d07f8b9'), + period: 365.26, + direction: 'counterclockwise', + planet_id: '65de67fb2e59d63e6d07f8b8', + // ... + } + +The following sample code shows how to access the related models by using +the dynamic properties as defined in the example classes: + +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin planet orbit dynamic property example + :end-before: end planet orbit dynamic property example + +.. _laravel-eloquent-relationship-one-to-many: + +One to Many Relationship +------------------------ + +A one to many relationship between models consists of a model that is +the parent and one or more related child model records. + +When you add a one to many relationship method, Eloquent lets you access the +model by using a dynamic property and stores the parent model's document ID +on each child model document. + +In {+odm-short+}, you can define a one to many relationship by adding the +``hasMany()`` method on the parent class and, optionally, the ``belongsTo()`` +method on the child class. + +When you add the inverse of the relationship by using the ``belongsTo()`` +method, Eloquent lets you access the parent model by using a dynamic property +without adding any fields. + +To learn more about one to many relationships, see +`One to Many `__ +in the Laravel documentation. + +One to Many Example +~~~~~~~~~~~~~~~~~~~ + +The following example class shows how to define a ``HasMany`` one to many +relationship between a ``Planet`` parent model and ``Moon`` child model by +using the ``hasMany()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/one-to-many/Planet.php + :language: php + :dedent: + +The following example class shows how to define the inverse ``BelongsTo`` +relationship between a ``Moon`` child model and the and the ``Planet`` parent +model by using the ``belongsTo()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/one-to-many/Moon.php + :language: php + :dedent: + +The following sample code shows how to instantiate a model for each class +and add the relationship between them. Click the :guilabel:`VIEW OUTPUT` +button to see the data created by running the code: + +.. io-code-block:: + + .. input:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin one-to-many save + :end-before: end one-to-many save + + .. output:: + :language: json + :visible: false + + // Parent document in the "planets" collection + { + _id: ObjectId('65dfb0050e323bbef800f7b2'), + name: 'Jupiter', + diameter_km: 142984, + // ... + } + + // Child documents in the "moons" collection + [ + { + _id: ObjectId('65dfb0050e323bbef800f7b3'), + name: 'Ganymede', + orbital_period: 7.15, + planet_id: '65dfb0050e323bbef800f7b2', + // ... + }, + { + _id: ObjectId('65dfb0050e323bbef800f7b4'), + name: 'Europa', + orbital_period: 3.55, + planet_id: '65dfb0050e323bbef800f7b2', + // ... + } + ] + +The following sample code shows how to access the related models by using +the dynamic properties as defined in the example classes. + +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin planet moons dynamic property example + :end-before: end planet moons dynamic property example + +.. _laravel-eloquent-relationship-many-to-many: + +Many to Many Relationship +------------------------- + +A many to many relationship consists of a relationship between two different +model types in which, for each type of model, an instance of the model can +be related to multiple instances of the other type. + +In {+odm-short+}, you can define a many to many relationship by adding the +``belongsToMany()`` method to both related classes. + +When you define a many to many relationship in a relational database, Laravel +creates a pivot table to track the relationships. When you use {+odm-short+}, +it omits the pivot table creation and adds the related document IDs to a +document field derived from the related model class name. + +.. tip:: + + Since {+odm-short+} uses a document field instead of a pivot table, omit + the pivot table parameter from the ``belongsToMany()`` constructor or set + it to ``null``. + +To learn more about many to many relationships in Laravel, see +`Many to Many `__ +in the Laravel documentation. + +The following section shows an example of how to create a many to many +relationship between model classes. + +Many to Many Example +~~~~~~~~~~~~~~~~~~~~ + +The following example class shows how to define a ``BelongsToMany`` many to +many relationship between a ``Planet`` and ``SpaceExplorer`` model by using +the ``belongsToMany()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/many-to-many/Planet.php + :language: php + :dedent: + +The following example class shows how to define the inverse ``BelongsToMany`` +many to many relationship between a ``SpaceExplorer`` and ``Planet`` model by +using the ``belongsToMany()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/many-to-many/SpaceExplorer.php + :language: php + :dedent: + +The following sample code shows how to instantiate a model for each class +and add the relationship between them. Click the :guilabel:`VIEW OUTPUT` +button to see the data created by running the code: + +.. io-code-block:: + + .. input:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin many-to-many save + :end-before: end many-to-many save + + .. output:: + :language: json + :visible: false + + // Documents in the "planets" collection + [ + { + _id: ObjectId('65e1043a5265269a03078ad0'), + name: 'Earth', + // ... + space_explorer_ids: [ + '65e1043b5265269a03078ad3', + '65e1043b5265269a03078ad4', + '65e1043b5265269a03078ad5' + ], + }, + { + _id: ObjectId('65e1043a5265269a03078ad1'), + name: 'Mars', + // ... + space_explorer_ids: [ '65e1043b5265269a03078ad4', '65e1043b5265269a03078ad5' ] + }, + { + _id: ObjectId('65e1043b5265269a03078ad2'), + name: 'Jupiter', + // ... + space_explorer_ids: [ '65e1043b5265269a03078ad3', '65e1043b5265269a03078ad5' ] + } + ] + + // Documents in the "space_explorers" collection + [ + { + _id: ObjectId('65e1043b5265269a03078ad3'), + name: 'Tanya Kirbuk', + // ... + planet_ids: [ '65e1043a5265269a03078ad0', '65e1043b5265269a03078ad2' ] + }, + { + _id: ObjectId('65e1043b5265269a03078ad4'), + name: 'Mark Watney', + // ... + planet_ids: [ '65e1043a5265269a03078ad0', '65e1043a5265269a03078ad1' ] + }, + { + _id: ObjectId('65e1043b5265269a03078ad5'), + name: 'Jean-Luc Picard', + // ... + planet_ids: [ + '65e1043a5265269a03078ad0', + '65e1043a5265269a03078ad1', + '65e1043b5265269a03078ad2' + ] + } + ] + +The following sample code shows how to access the related models by using +the dynamic properties as defined in the example classes. + +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin many-to-many dynamic property example + :end-before: end many-to-many dynamic property example + +.. _laravel-embedded-document-pattern: + +Embedded Document Pattern +------------------------- + +In MongoDB, the embedded document pattern adds the related model's data into +the parent model instead of keeping foreign key references. Use this pattern +to meet one or more of the following requirements: + +- Keeping associated data together in a single collection +- Performing atomic updates on multiple fields of the document and the associated + data +- Reducing the number of reads required to fetch the data + +In {+odm-short+}, you can define embedded documents by adding one of the +following methods: + +- ``embedsOne()`` to embed a single document +- ``embedsMany()`` to embed multiple documents + +.. note:: + + These methods return Eloquent collections, which differ from query builder + objects. + +To learn more about the MongoDB embedded document pattern, see the following +MongoDB server tutorials: + +- :manual:`Model One-to-One Relationships with Embedded Documents ` +- :manual:`Model One-to-Many Relationships with Embedded Documents ` + +The following section shows an example of how to use the embedded document +pattern. + +Embedded Document Example +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example class shows how to define an ``EmbedsMany`` one to many +relationship between a ``SpaceShip`` and ``Cargo`` model by using the +``embedsMany()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/embeds/SpaceShip.php + :language: php + :dedent: + +The embedded model class omits the relationship definition as shown in the +following ``Cargo`` model class: + +.. literalinclude:: /includes/eloquent-models/relationships/embeds/Cargo.php + :language: php + :dedent: + +The following sample code shows how to create a ``SpaceShip`` model and +embed multiple ``Cargo`` models and the MongoDB document created by running the +code. Click the :guilabel:`VIEW OUTPUT` button to see the data created by +running the code: + +.. io-code-block:: + + .. input:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin embedsMany save + :end-before: end embedsMany save + + .. output:: + :language: json + :visible: false + + // Document in the "space_ships" collection + { + _id: ObjectId('65e207b9aa167d29a3048853'), + name: 'The Millenium Falcon', + // ... + cargo: [ + { + name: 'spice', + weight: 50, + // ... + _id: ObjectId('65e207b9aa167d29a3048854') + }, + { + name: 'hyperdrive', + weight: 25, + // ... + _id: ObjectId('65e207b9aa167d29a3048855') + } + ] + } + +.. _laravel-relationship-cross-database: + +Cross-Database Relationships +---------------------------- + +A cross-database relationship in {+odm-short+} is a relationship between models +stored in a relational database and models stored in a MongoDB database. + +When you add a cross-database relationship, Eloquent lets you access the +related models by using a dynamic property. + +{+odm-short+} supports the following cross-database relationship methods: + +- ``hasOne()`` +- ``hasMany()`` +- ``belongsTo()`` + +To define a cross-database relationship, you must import the +``MongoDB\Laravel\Eloquent\HybridRelations`` package in the class stored in +the relational database. + +The following section shows an example of how to define a cross-database +relationship. + +Cross-Database Relationship Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example class shows how to define a ``HasMany`` relationship +between a ``SpaceShip`` model stored in a relational database and a +``Passenger`` model stored in a MongoDB database: + +.. literalinclude:: /includes/eloquent-models/relationships/cross-db/SpaceShip.php + :language: php + :dedent: + +The following example class shows how to define the inverse ``BelongsTo`` +relationship between a ``Passenger`` model and the and the ``Spaceship`` +model by using the ``belongsTo()`` method: + +.. literalinclude:: /includes/eloquent-models/relationships/cross-db/Passenger.php + :language: php + :dedent: + +.. tip:: + + Make sure that the primary key defined in your relational database table + schema matches the one that your model uses. To learn more about Laravel + primary keys and schema definitions, see the following pages in the Laravel + documentation: + + - `Primary Keys `__ + - `Database: Migrations `__ + +The following sample code shows how to create a ``SpaceShip`` model in +a MySQL database and related ``Passenger`` models in a MongoDB database as well +as the data created by running the code. Click the :guilabel:`VIEW OUTPUT` button +to see the data created by running the code: + +.. io-code-block:: + + .. input:: /includes/eloquent-models/relationships/RelationshipController.php + :language: php + :dedent: + :start-after: begin cross-database save + :end-before: end cross-database save + + .. output:: + :language: none + :visible: false + + -- Row in the "space_ships" table + +------+----------+ + | id | name | + +------+----------+ + | 1234 | Nostromo | + +------+----------+ + + // Document in the "passengers" collection + [ + { + _id: ObjectId('65e625e74903fd63af0a5524'), + name: 'Ellen Ripley', + space_ship_id: 1234, + // ... + }, + { + _id: ObjectId('65e625e74903fd63af0a5525'), + name: 'Dwayne Hicks', + space_ship_id: 1234, + // ... + } + ] + diff --git a/docs/includes/eloquent-models/relationships/RelationshipController.php b/docs/includes/eloquent-models/relationships/RelationshipController.php new file mode 100644 index 000000000..fc10184d3 --- /dev/null +++ b/docs/includes/eloquent-models/relationships/RelationshipController.php @@ -0,0 +1,194 @@ +name = 'Earth'; + $planet->diameter_km = 12742; + $planet->save(); + + $orbit = new Orbit(); + $orbit->period = 365.26; + $orbit->direction = 'counterclockwise'; + + $planet->orbit()->save($orbit); + // end one-to-one save + } + + private function oneToMany() + { + // begin one-to-many save + $planet = new Planet(); + $planet->name = 'Jupiter'; + $planet->diameter_km = 142984; + $planet->save(); + + $moon1 = new Moon(); + $moon1->name = 'Ganymede'; + $moon1->orbital_period = 7.15; + + $moon2 = new Moon(); + $moon2->name = 'Europa'; + $moon2->orbital_period = 3.55; + + $planet->moons()->save($moon1); + $planet->moons()->save($moon2); + // end one-to-many save + } + + private function planetOrbitDynamic() + { + // begin planet orbit dynamic property example + $planet = Planet::first(); + $relatedOrbit = $planet->orbit; + + $orbit = Orbit::first(); + $relatedPlanet = $orbit->planet; + // end planet orbit dynamic property example + } + + private function planetMoonsDynamic() + { + // begin planet moons dynamic property example + $planet = Planet::first(); + $relatedMoons = $planet->moons; + + $moon = Moon::first(); + $relatedPlanet = $moon->planet; + // end planet moons dynamic property example + } + + private function manyToMany() + { + // begin many-to-many save + $planetEarth = new Planet(); + $planetEarth->name = 'Earth'; + $planetEarth->save(); + + $planetMars = new Planet(); + $planetMars->name = 'Mars'; + $planetMars->save(); + + $planetJupiter = new Planet(); + $planetJupiter->name = 'Jupiter'; + $planetJupiter->save(); + + $explorerTanya = new SpaceExplorer(); + $explorerTanya->name = 'Tanya Kirbuk'; + $explorerTanya->save(); + + $explorerMark = new SpaceExplorer(); + $explorerMark->name = 'Mark Watney'; + $explorerMark->save(); + + $explorerJeanluc = new SpaceExplorer(); + $explorerJeanluc->name = 'Jean-Luc Picard'; + $explorerJeanluc->save(); + + $explorerTanya->planetsVisited()->attach($planetEarth); + $explorerTanya->planetsVisited()->attach($planetJupiter); + $explorerMark->planetsVisited()->attach($planetEarth); + $explorerMark->planetsVisited()->attach($planetMars); + $explorerJeanluc->planetsVisited()->attach($planetEarth); + $explorerJeanluc->planetsVisited()->attach($planetMars); + $explorerJeanluc->planetsVisited()->attach($planetJupiter); + // end many-to-many save + } + + private function manyToManyDynamic() + { + // begin many-to-many dynamic property example + $planet = Planet::first(); + $explorers = $planet->visitors; + + $spaceExplorer = SpaceExplorer::first(); + $explored = $spaceExplorer->planetsVisited; + // end many-to-many dynamic property example + } + + private function embedsMany() + { + // begin embedsMany save + $spaceship = new SpaceShip(); + $spaceship->name = 'The Millenium Falcon'; + $spaceship->save(); + + $cargoSpice = new Cargo(); + $cargoSpice->name = 'spice'; + $cargoSpice->weight = 50; + + $cargoHyperdrive = new Cargo(); + $cargoHyperdrive->name = 'hyperdrive'; + $cargoHyperdrive->weight = 25; + + $spaceship->cargo()->attach($cargoSpice); + $spaceship->cargo()->attach($cargoHyperdrive); + // end embedsMany save + } + + private function crossDatabase() + { + // begin cross-database save + $spaceship = new SpaceShip(); + $spaceship->id = 1234; + $spaceship->name = 'Nostromo'; + $spaceship->save(); + + $passengerEllen = new Passenger(); + $passengerEllen->name = 'Ellen Ripley'; + + $passengerDwayne = new Passenger(); + $passengerDwayne->name = 'Dwayne Hicks'; + + $spaceship->passengers()->save($passengerEllen); + $spaceship->passengers()->save($passengerDwayne); + // end cross-database save + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + } + + /** + * Display the specified resource. + */ + public function show() + { + return 'ok'; + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Planet $planet) + { + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, Planet $planet) + { + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(Planet $planet) + { + } +} diff --git a/docs/includes/eloquent-models/relationships/cross-db/Passenger.php b/docs/includes/eloquent-models/relationships/cross-db/Passenger.php new file mode 100644 index 000000000..4ceb7c45b --- /dev/null +++ b/docs/includes/eloquent-models/relationships/cross-db/Passenger.php @@ -0,0 +1,18 @@ +belongsTo(SpaceShip::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/cross-db/SpaceShip.php b/docs/includes/eloquent-models/relationships/cross-db/SpaceShip.php new file mode 100644 index 000000000..1f3c5d120 --- /dev/null +++ b/docs/includes/eloquent-models/relationships/cross-db/SpaceShip.php @@ -0,0 +1,21 @@ +hasMany(Passenger::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/embeds/Cargo.php b/docs/includes/eloquent-models/relationships/embeds/Cargo.php new file mode 100644 index 000000000..3ce144815 --- /dev/null +++ b/docs/includes/eloquent-models/relationships/embeds/Cargo.php @@ -0,0 +1,12 @@ +embedsMany(Cargo::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/many-to-many/Planet.php b/docs/includes/eloquent-models/relationships/many-to-many/Planet.php new file mode 100644 index 000000000..4059d634d --- /dev/null +++ b/docs/includes/eloquent-models/relationships/many-to-many/Planet.php @@ -0,0 +1,18 @@ +belongsToMany(SpaceExplorer::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/many-to-many/SpaceExplorer.php b/docs/includes/eloquent-models/relationships/many-to-many/SpaceExplorer.php new file mode 100644 index 000000000..aa9b2829d --- /dev/null +++ b/docs/includes/eloquent-models/relationships/many-to-many/SpaceExplorer.php @@ -0,0 +1,18 @@ +belongsToMany(Planet::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/one-to-many/Moon.php b/docs/includes/eloquent-models/relationships/one-to-many/Moon.php new file mode 100644 index 000000000..ca5b7ae7c --- /dev/null +++ b/docs/includes/eloquent-models/relationships/one-to-many/Moon.php @@ -0,0 +1,18 @@ +belongsTo(Planet::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/one-to-many/Planet.php b/docs/includes/eloquent-models/relationships/one-to-many/Planet.php new file mode 100644 index 000000000..679877ae5 --- /dev/null +++ b/docs/includes/eloquent-models/relationships/one-to-many/Planet.php @@ -0,0 +1,18 @@ +hasMany(Moon::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/one-to-one/Orbit.php b/docs/includes/eloquent-models/relationships/one-to-one/Orbit.php new file mode 100644 index 000000000..4cb526309 --- /dev/null +++ b/docs/includes/eloquent-models/relationships/one-to-one/Orbit.php @@ -0,0 +1,18 @@ +belongsTo(Planet::class); + } +} diff --git a/docs/includes/eloquent-models/relationships/one-to-one/Planet.php b/docs/includes/eloquent-models/relationships/one-to-one/Planet.php new file mode 100644 index 000000000..e3137ab3a --- /dev/null +++ b/docs/includes/eloquent-models/relationships/one-to-one/Planet.php @@ -0,0 +1,18 @@ +hasOne(Orbit::class); + } +} From 260620e61a9226c9f98cc1e751b569d00413be85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 26 Mar 2024 16:06:43 +0100 Subject: [PATCH 04/14] Add tests to doc examples (#2775) --- docs/eloquent-models/relationships.txt | 16 +- .../relationships/RelationshipController.php | 194 ------------- .../RelationshipsExamplesTest.php | 265 ++++++++++++++++++ .../relationships/cross-db/Passenger.php | 2 +- 4 files changed, 274 insertions(+), 203 deletions(-) delete mode 100644 docs/includes/eloquent-models/relationships/RelationshipController.php create mode 100644 docs/includes/eloquent-models/relationships/RelationshipsExamplesTest.php diff --git a/docs/eloquent-models/relationships.txt b/docs/eloquent-models/relationships.txt index 92625f076..2ae716132 100644 --- a/docs/eloquent-models/relationships.txt +++ b/docs/eloquent-models/relationships.txt @@ -95,7 +95,7 @@ button to see the data created by running the code: .. io-code-block:: - .. input:: /includes/eloquent-models/relationships/RelationshipController.php + .. input:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin one-to-one save @@ -125,7 +125,7 @@ button to see the data created by running the code: The following sample code shows how to access the related models by using the dynamic properties as defined in the example classes: -.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin planet orbit dynamic property example @@ -180,7 +180,7 @@ button to see the data created by running the code: .. io-code-block:: - .. input:: /includes/eloquent-models/relationships/RelationshipController.php + .. input:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin one-to-many save @@ -219,7 +219,7 @@ button to see the data created by running the code: The following sample code shows how to access the related models by using the dynamic properties as defined in the example classes. -.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin planet moons dynamic property example @@ -280,7 +280,7 @@ button to see the data created by running the code: .. io-code-block:: - .. input:: /includes/eloquent-models/relationships/RelationshipController.php + .. input:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin many-to-many save @@ -345,7 +345,7 @@ button to see the data created by running the code: The following sample code shows how to access the related models by using the dynamic properties as defined in the example classes. -.. literalinclude:: /includes/eloquent-models/relationships/RelationshipController.php +.. literalinclude:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin many-to-many dynamic property example @@ -410,7 +410,7 @@ running the code: .. io-code-block:: - .. input:: /includes/eloquent-models/relationships/RelationshipController.php + .. input:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin embedsMany save @@ -501,7 +501,7 @@ to see the data created by running the code: .. io-code-block:: - .. input:: /includes/eloquent-models/relationships/RelationshipController.php + .. input:: /includes/eloquent-models/relationships/RelationshipsExamplesTest.php :language: php :dedent: :start-after: begin cross-database save diff --git a/docs/includes/eloquent-models/relationships/RelationshipController.php b/docs/includes/eloquent-models/relationships/RelationshipController.php deleted file mode 100644 index fc10184d3..000000000 --- a/docs/includes/eloquent-models/relationships/RelationshipController.php +++ /dev/null @@ -1,194 +0,0 @@ -name = 'Earth'; - $planet->diameter_km = 12742; - $planet->save(); - - $orbit = new Orbit(); - $orbit->period = 365.26; - $orbit->direction = 'counterclockwise'; - - $planet->orbit()->save($orbit); - // end one-to-one save - } - - private function oneToMany() - { - // begin one-to-many save - $planet = new Planet(); - $planet->name = 'Jupiter'; - $planet->diameter_km = 142984; - $planet->save(); - - $moon1 = new Moon(); - $moon1->name = 'Ganymede'; - $moon1->orbital_period = 7.15; - - $moon2 = new Moon(); - $moon2->name = 'Europa'; - $moon2->orbital_period = 3.55; - - $planet->moons()->save($moon1); - $planet->moons()->save($moon2); - // end one-to-many save - } - - private function planetOrbitDynamic() - { - // begin planet orbit dynamic property example - $planet = Planet::first(); - $relatedOrbit = $planet->orbit; - - $orbit = Orbit::first(); - $relatedPlanet = $orbit->planet; - // end planet orbit dynamic property example - } - - private function planetMoonsDynamic() - { - // begin planet moons dynamic property example - $planet = Planet::first(); - $relatedMoons = $planet->moons; - - $moon = Moon::first(); - $relatedPlanet = $moon->planet; - // end planet moons dynamic property example - } - - private function manyToMany() - { - // begin many-to-many save - $planetEarth = new Planet(); - $planetEarth->name = 'Earth'; - $planetEarth->save(); - - $planetMars = new Planet(); - $planetMars->name = 'Mars'; - $planetMars->save(); - - $planetJupiter = new Planet(); - $planetJupiter->name = 'Jupiter'; - $planetJupiter->save(); - - $explorerTanya = new SpaceExplorer(); - $explorerTanya->name = 'Tanya Kirbuk'; - $explorerTanya->save(); - - $explorerMark = new SpaceExplorer(); - $explorerMark->name = 'Mark Watney'; - $explorerMark->save(); - - $explorerJeanluc = new SpaceExplorer(); - $explorerJeanluc->name = 'Jean-Luc Picard'; - $explorerJeanluc->save(); - - $explorerTanya->planetsVisited()->attach($planetEarth); - $explorerTanya->planetsVisited()->attach($planetJupiter); - $explorerMark->planetsVisited()->attach($planetEarth); - $explorerMark->planetsVisited()->attach($planetMars); - $explorerJeanluc->planetsVisited()->attach($planetEarth); - $explorerJeanluc->planetsVisited()->attach($planetMars); - $explorerJeanluc->planetsVisited()->attach($planetJupiter); - // end many-to-many save - } - - private function manyToManyDynamic() - { - // begin many-to-many dynamic property example - $planet = Planet::first(); - $explorers = $planet->visitors; - - $spaceExplorer = SpaceExplorer::first(); - $explored = $spaceExplorer->planetsVisited; - // end many-to-many dynamic property example - } - - private function embedsMany() - { - // begin embedsMany save - $spaceship = new SpaceShip(); - $spaceship->name = 'The Millenium Falcon'; - $spaceship->save(); - - $cargoSpice = new Cargo(); - $cargoSpice->name = 'spice'; - $cargoSpice->weight = 50; - - $cargoHyperdrive = new Cargo(); - $cargoHyperdrive->name = 'hyperdrive'; - $cargoHyperdrive->weight = 25; - - $spaceship->cargo()->attach($cargoSpice); - $spaceship->cargo()->attach($cargoHyperdrive); - // end embedsMany save - } - - private function crossDatabase() - { - // begin cross-database save - $spaceship = new SpaceShip(); - $spaceship->id = 1234; - $spaceship->name = 'Nostromo'; - $spaceship->save(); - - $passengerEllen = new Passenger(); - $passengerEllen->name = 'Ellen Ripley'; - - $passengerDwayne = new Passenger(); - $passengerDwayne->name = 'Dwayne Hicks'; - - $spaceship->passengers()->save($passengerEllen); - $spaceship->passengers()->save($passengerDwayne); - // end cross-database save - } - - /** - * Store a newly created resource in storage. - */ - public function store(Request $request) - { - } - - /** - * Display the specified resource. - */ - public function show() - { - return 'ok'; - } - - /** - * Show the form for editing the specified resource. - */ - public function edit(Planet $planet) - { - } - - /** - * Update the specified resource in storage. - */ - public function update(Request $request, Planet $planet) - { - } - - /** - * Remove the specified resource from storage. - */ - public function destroy(Planet $planet) - { - } -} diff --git a/docs/includes/eloquent-models/relationships/RelationshipsExamplesTest.php b/docs/includes/eloquent-models/relationships/RelationshipsExamplesTest.php new file mode 100644 index 000000000..51876416a --- /dev/null +++ b/docs/includes/eloquent-models/relationships/RelationshipsExamplesTest.php @@ -0,0 +1,265 @@ +name = 'Earth'; + $planet->diameter_km = 12742; + $planet->save(); + + $orbit = new Orbit(); + $orbit->period = 365.26; + $orbit->direction = 'counterclockwise'; + + $planet->orbit()->save($orbit); + // end one-to-one save + + $planet = Planet::first(); + $this->assertInstanceOf(Planet::class, $planet); + $this->assertInstanceOf(Orbit::class, $planet->orbit); + + // begin planet orbit dynamic property example + $planet = Planet::first(); + $relatedOrbit = $planet->orbit; + + $orbit = Orbit::first(); + $relatedPlanet = $orbit->planet; + // end planet orbit dynamic property example + + $this->assertInstanceOf(Orbit::class, $relatedOrbit); + $this->assertInstanceOf(Planet::class, $relatedPlanet); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testOneToMany(): void + { + require_once __DIR__ . '/one-to-many/Planet.php'; + require_once __DIR__ . '/one-to-many/Moon.php'; + + // Clear the database + Planet::truncate(); + Moon::truncate(); + + // begin one-to-many save + $planet = new Planet(); + $planet->name = 'Jupiter'; + $planet->diameter_km = 142984; + $planet->save(); + + $moon1 = new Moon(); + $moon1->name = 'Ganymede'; + $moon1->orbital_period = 7.15; + + $moon2 = new Moon(); + $moon2->name = 'Europa'; + $moon2->orbital_period = 3.55; + + $planet->moons()->save($moon1); + $planet->moons()->save($moon2); + // end one-to-many save + + $planet = Planet::first(); + $this->assertInstanceOf(Planet::class, $planet); + $this->assertCount(2, $planet->moons); + $this->assertInstanceOf(Moon::class, $planet->moons->first()); + + // begin planet moons dynamic property example + $planet = Planet::first(); + $relatedMoons = $planet->moons; + + $moon = Moon::first(); + $relatedPlanet = $moon->planet; + // end planet moons dynamic property example + + $this->assertInstanceOf(Moon::class, $relatedMoons->first()); + $this->assertInstanceOf(Planet::class, $relatedPlanet); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testManyToMany(): void + { + require_once __DIR__ . '/many-to-many/Planet.php'; + require_once __DIR__ . '/many-to-many/SpaceExplorer.php'; + + // Clear the database + Planet::truncate(); + SpaceExplorer::truncate(); + + // begin many-to-many save + $planetEarth = new Planet(); + $planetEarth->name = 'Earth'; + $planetEarth->save(); + + $planetMars = new Planet(); + $planetMars->name = 'Mars'; + $planetMars->save(); + + $planetJupiter = new Planet(); + $planetJupiter->name = 'Jupiter'; + $planetJupiter->save(); + + $explorerTanya = new SpaceExplorer(); + $explorerTanya->name = 'Tanya Kirbuk'; + $explorerTanya->save(); + + $explorerMark = new SpaceExplorer(); + $explorerMark->name = 'Mark Watney'; + $explorerMark->save(); + + $explorerJeanluc = new SpaceExplorer(); + $explorerJeanluc->name = 'Jean-Luc Picard'; + $explorerJeanluc->save(); + + $explorerTanya->planetsVisited()->attach($planetEarth); + $explorerTanya->planetsVisited()->attach($planetJupiter); + $explorerMark->planetsVisited()->attach($planetEarth); + $explorerMark->planetsVisited()->attach($planetMars); + $explorerJeanluc->planetsVisited()->attach($planetEarth); + $explorerJeanluc->planetsVisited()->attach($planetMars); + $explorerJeanluc->planetsVisited()->attach($planetJupiter); + // end many-to-many save + + $planet = Planet::where('name', 'Earth')->first(); + $this->assertInstanceOf(Planet::class, $planet); + $this->assertCount(3, $planet->visitors); + $this->assertInstanceOf(SpaceExplorer::class, $planet->visitors->first()); + + $explorer = SpaceExplorer::where('name', 'Jean-Luc Picard')->first(); + $this->assertInstanceOf(SpaceExplorer::class, $explorer); + $this->assertCount(3, $explorer->planetsVisited); + $this->assertInstanceOf(Planet::class, $explorer->planetsVisited->first()); + + // begin many-to-many dynamic property example + $planet = Planet::first(); + $explorers = $planet->visitors; + + $spaceExplorer = SpaceExplorer::first(); + $explored = $spaceExplorer->planetsVisited; + // end many-to-many dynamic property example + + $this->assertCount(3, $explorers); + $this->assertInstanceOf(SpaceExplorer::class, $explorers->first()); + $this->assertCount(2, $explored); + $this->assertInstanceOf(Planet::class, $explored->first()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testEmbedsMany(): void + { + require_once __DIR__ . '/embeds/Cargo.php'; + require_once __DIR__ . '/embeds/SpaceShip.php'; + + // Clear the database + SpaceShip::truncate(); + + // begin embedsMany save + $spaceship = new SpaceShip(); + $spaceship->name = 'The Millenium Falcon'; + $spaceship->save(); + + $cargoSpice = new Cargo(); + $cargoSpice->name = 'spice'; + $cargoSpice->weight = 50; + + $cargoHyperdrive = new Cargo(); + $cargoHyperdrive->name = 'hyperdrive'; + $cargoHyperdrive->weight = 25; + + $spaceship->cargo()->attach($cargoSpice); + $spaceship->cargo()->attach($cargoHyperdrive); + // end embedsMany save + + $spaceship = SpaceShip::first(); + $this->assertInstanceOf(SpaceShip::class, $spaceship); + $this->assertCount(2, $spaceship->cargo); + $this->assertInstanceOf(Cargo::class, $spaceship->cargo->first()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testCrossDatabase(): void + { + require_once __DIR__ . '/cross-db/Passenger.php'; + require_once __DIR__ . '/cross-db/SpaceShip.php'; + + $schema = Schema::connection('sqlite'); + assert($schema instanceof SQLiteBuilder); + + $schema->dropIfExists('space_ships'); + $schema->create('space_ships', function (Blueprint $table) { + $table->id(); + $table->string('name'); + $table->timestamps(); + }); + + // Clear the database + Passenger::truncate(); + + // begin cross-database save + $spaceship = new SpaceShip(); + $spaceship->id = 1234; + $spaceship->name = 'Nostromo'; + $spaceship->save(); + + $passengerEllen = new Passenger(); + $passengerEllen->name = 'Ellen Ripley'; + + $passengerDwayne = new Passenger(); + $passengerDwayne->name = 'Dwayne Hicks'; + + $spaceship->passengers()->save($passengerEllen); + $spaceship->passengers()->save($passengerDwayne); + // end cross-database save + + $spaceship = SpaceShip::first(); + $this->assertInstanceOf(SpaceShip::class, $spaceship); + $this->assertCount(2, $spaceship->passengers); + $this->assertInstanceOf(Passenger::class, $spaceship->passengers->first()); + + $passenger = Passenger::first(); + $this->assertInstanceOf(Passenger::class, $passenger); + } +} diff --git a/docs/includes/eloquent-models/relationships/cross-db/Passenger.php b/docs/includes/eloquent-models/relationships/cross-db/Passenger.php index 4ceb7c45b..3379c866b 100644 --- a/docs/includes/eloquent-models/relationships/cross-db/Passenger.php +++ b/docs/includes/eloquent-models/relationships/cross-db/Passenger.php @@ -4,8 +4,8 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use MongoDB\Laravel\Eloquent\Model; -use MongoDB\Laravel\Relations\BelongsTo; class Passenger extends Model { From 860dfaf9fd9d39693367aba5fa97e5f00067762a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 2 Apr 2024 15:30:17 +0200 Subject: [PATCH 05/14] PHPORM-160 Run doc tests (#2795) --- phpunit.xml.dist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b1aa3a8eb..5431164d8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,6 +12,9 @@ tests/ + + docs/includes/ + From 9a616f9b30efe8645d0bf72f5597ad75979a6a5a Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Wed, 3 Apr 2024 10:24:41 -0400 Subject: [PATCH 06/14] DOCSP-37618: Usage Examples landing page (#2767) * DOCSP-37618: Usage Examples landing page * fix refs * update TOC * format fix * CC feedback * remove info * add back * typo * CC feedback 2 * add section toc * feedback, removing tabs * workflow file * edits * fix * more CC feedback * run -> use * changes to running instructions * turn back into steps * small fixes * more reworking * reword * reworking * feedback * newline * remove file * edits * fix --- docs/index.txt | 9 ++++- docs/quick-start/view-data.txt | 2 +- docs/usage-examples.txt | 68 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 docs/usage-examples.txt diff --git a/docs/index.txt b/docs/index.txt index febdb9371..ec6825419 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -14,6 +14,7 @@ Laravel MongoDB :maxdepth: 1 /quick-start + /usage-examples Release Notes /retrieve /eloquent-models @@ -47,10 +48,16 @@ Learn how to add {+odm-short+} to a Laravel web application, connect to MongoDB hosted on MongoDB Atlas, and begin working with data in the :ref:`laravel-quick-start` section. +Usage Examples +-------------- + +See fully runnable code examples and explanations of common +MongoDB operations in the :ref:`laravel-usage-examples` section. + Fundamentals ------------ -To learn how to perform the following tasks by using the {+odm-short+}, +To learn how to perform the following tasks by using {+odm-short+}, see the following content: - :ref:`laravel-fundamentals-retrieve` diff --git a/docs/quick-start/view-data.txt b/docs/quick-start/view-data.txt index 35d53368c..1be17bb3f 100644 --- a/docs/quick-start/view-data.txt +++ b/docs/quick-start/view-data.txt @@ -1,4 +1,4 @@ -.. laravel-quick-start-view-data: +.. _laravel-quick-start-view-data: ================= View MongoDB Data diff --git a/docs/usage-examples.txt b/docs/usage-examples.txt new file mode 100644 index 000000000..bf14bba4a --- /dev/null +++ b/docs/usage-examples.txt @@ -0,0 +1,68 @@ +.. _laravel-usage-examples: + +============== +Usage Examples +============== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: set up, runnable + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +Usage examples show runnable code examples to demonstrate frequently used MongoDB +operations. Each usage example includes the following components: + +- Explanation of the MongoDB operation +- Example code that you can run from an application controller +- Output displayed by the print statement + +How to Use the Usage Examples +----------------------------- + +To learn how to add a usage example to your Laravel application and view the expected output, +see the following sections: + +- :ref:`before-start` +- :ref:`run-usage-examples` + +.. _before-start: + +Before You Get Started +~~~~~~~~~~~~~~~~~~~~~~ + +You can run the usage examples from your own Laravel application or from the +``{+quickstart-app-name+}`` application created in the :ref:`laravel-quick-start` guide. + +The usage examples are designed to run operations on a MongoDB deployment that contains +the MongoDB Atlas sample datasets. Before running the usage examples, ensure that you load +the sample data into the MongoDB cluster to which your application connects. Otherwise, the +operation output might not match the text included in the ``{+code-output-label+}`` tab of +the usage example page. + +.. tip:: + + For instructions on loading the sample data into a MongoDB cluster, see + :atlas:`Load Sample Data ` in the Atlas documentation. + +.. _run-usage-examples: + +Run the Usage Example +~~~~~~~~~~~~~~~~~~~~~ + +Each usage example page includes sample code that demonstrates a MongoDB operation and prints +a result. To run the operation, you can copy the sample code to a controller endpoint in your +Laravel application. + +To view the expected output of the operation, you can add a web route to your application that +calls the controller function and returns the result to a web interface. \ No newline at end of file From 8eb9271201795931bc54e4e06e04a31f581485cb Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Wed, 3 Apr 2024 10:51:03 -0400 Subject: [PATCH 07/14] DOCSP-35974: Update one usage example (#2781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DOCSP-35974: Update one usage example * fixes * other usage ex changes * reworking the example * add tip, spacing * restructuring * reword tip * fixes * edits * reword * add more running info * small change * edits * source constant * test file * move test file * single quotes * print syntax * print statement again * JT feedback * apply phpcbf formatting * code * apply phpcbf formatting * JT feedback * Fix UpdateOneTest example * add to TOC --------- Co-authored-by: norareidy Co-authored-by: Jérôme Tamarelle --- docs/includes/usage-examples/Movie.php | 12 ++++ .../includes/usage-examples/UpdateOneTest.php | 48 +++++++++++++ docs/usage-examples.txt | 8 ++- docs/usage-examples/updateOne.txt | 67 +++++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 docs/includes/usage-examples/Movie.php create mode 100644 docs/includes/usage-examples/UpdateOneTest.php create mode 100644 docs/usage-examples/updateOne.txt diff --git a/docs/includes/usage-examples/Movie.php b/docs/includes/usage-examples/Movie.php new file mode 100644 index 000000000..728a066de --- /dev/null +++ b/docs/includes/usage-examples/Movie.php @@ -0,0 +1,12 @@ + 'Carol', + 'imdb' => [ + 'rating' => 7.2, + 'votes' => 125000, + ], + ], + ]); + + // begin-update-one + $updates = Movie::where('title', 'Carol') + ->orderBy('_id') + ->first() + ->update([ + 'imdb' => [ + 'rating' => 7.3, + 'votes' => 142000, + ], + ]); + + echo 'Updated documents: ' . $updates; + // end-update-one + + $this->assertTrue($updates); + $this->expectOutputString('Updated documents: 1'); + } +} diff --git a/docs/usage-examples.txt b/docs/usage-examples.txt index bf14bba4a..08dda77ea 100644 --- a/docs/usage-examples.txt +++ b/docs/usage-examples.txt @@ -65,4 +65,10 @@ a result. To run the operation, you can copy the sample code to a controller end Laravel application. To view the expected output of the operation, you can add a web route to your application that -calls the controller function and returns the result to a web interface. \ No newline at end of file +calls the controller function and returns the result to a web interface. + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + /usage-examples/updateOne \ No newline at end of file diff --git a/docs/usage-examples/updateOne.txt b/docs/usage-examples/updateOne.txt new file mode 100644 index 000000000..f60bd3bad --- /dev/null +++ b/docs/usage-examples/updateOne.txt @@ -0,0 +1,67 @@ +.. _laravel-update-one-usage: + +================= +Update a Document +================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: update one, modify, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +You can update a document in a collection by retrieving a single document and calling +the ``update()`` method on an Eloquent model or a query builder. + +Pass a query filter to the ``where()`` method, sort the matching documents, and call the +``first()`` method to retrieve only the first document. Then, update this matching document +by passing your intended document changes to the ``update()`` method. + +Example +------- + +This usage example performs the following actions: + +- Uses the ``Movie`` Eloquent model to represent the ``movies`` collection in the + ``sample_mflix`` database. +- Updates a document from the ``movies`` collection that matches a query filter. + +The example calls the following methods on the ``Movie`` model: + +- ``where()``: matches documents in which the value of the ``title`` field is ``'Carol'``. +- ``orderBy()``: sorts matched documents by their ascending ``_id`` values. +- ``first()``: retrieves only the first matching document. +- ``update()``: updates the value of the ``imdb.rating`` nested field to from ``6.9`` to + ``7.3``. This method also updates the ``imdb.votes`` nested field from ``493`` to ``142000``. + +.. io-code-block:: + :copyable: true + + .. input:: ../includes/usage-examples/UpdateOneTest.php + :start-after: begin-update-one + :end-before: end-update-one + :language: php + :dedent: + + .. output:: + :language: console + :visible: false + + Updated documents: 1 + +For instructions on editing your Laravel application to run the usage example, see the +:ref:`Usage Example landing page `. + +.. tip:: + + To learn more about updating data with {+odm-short+}, see the `Updates + `__ section of the + Laravel documentation. + From e338fc20674453c00dc720d1659a66abf4eb083d Mon Sep 17 00:00:00 2001 From: Chris Cho Date: Wed, 3 Apr 2024 11:54:36 -0400 Subject: [PATCH 08/14] DOCSP-38076 quickstart updates for 11 (#2799) * DOCSP-38076: quick start changes for Laravel 11 --- docs/quick-start.txt | 2 +- docs/quick-start/configure-mongodb.txt | 11 ++++++++--- docs/quick-start/view-data.txt | 14 +++++++------- docs/quick-start/write-data.txt | 17 ++++++++++++++--- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/docs/quick-start.txt b/docs/quick-start.txt index d672f3e31..fb8ad6fe2 100644 --- a/docs/quick-start.txt +++ b/docs/quick-start.txt @@ -53,7 +53,7 @@ that connects to a MongoDB deployment. .. tip:: You can download the complete web application project by cloning the - `laravel-quickstart `__ + `laravel-quickstart `__ GitHub repository. .. button:: Next: Download and Install diff --git a/docs/quick-start/configure-mongodb.txt b/docs/quick-start/configure-mongodb.txt index 66cd2380c..2f6dd0e36 100644 --- a/docs/quick-start/configure-mongodb.txt +++ b/docs/quick-start/configure-mongodb.txt @@ -41,15 +41,20 @@ Configure Your MongoDB Connection // ... - .. step:: Add the Laravel MongoDB provider + .. step:: Add the MongoDB provider - Open the ``app.php`` file in the ``config`` directory and - add the following entry into the ``providers`` array: + Open the ``providers.php`` file in the ``bootstrap`` directory and add + the following entry into the array: .. code-block:: MongoDB\Laravel\MongoDBServiceProvider::class, + .. tip:: + + To learn how to register the provider in Laravel 10.x, see + `Registering Providers `__. + After completing these steps, your Laravel web application is ready to connect to MongoDB. diff --git a/docs/quick-start/view-data.txt b/docs/quick-start/view-data.txt index 35d53368c..52c0e5dc4 100644 --- a/docs/quick-start/view-data.txt +++ b/docs/quick-start/view-data.txt @@ -67,13 +67,13 @@ View MongoDB Data public function show() { - return view('browse_movies', [ - 'movies' => Movie::where('runtime', '<', 60) - ->where('imdb.rating', '>', 8.5) - ->orderBy('imdb.rating', 'desc') - ->take(10) - ->get() - ]); + return view('browse_movies', [ + 'movies' => Movie::where('runtime', '<', 60) + ->where('imdb.rating', '>', 8.5) + ->orderBy('imdb.rating', 'desc') + ->take(10) + ->get() + ]); } .. step:: Add a web route diff --git a/docs/quick-start/write-data.txt b/docs/quick-start/write-data.txt index 31568286a..ddf2a98d9 100644 --- a/docs/quick-start/write-data.txt +++ b/docs/quick-start/write-data.txt @@ -32,6 +32,17 @@ Write Data to MongoDB .. step:: Add an API route that calls the controller function + Generate an API route file by running the following command: + + .. code-block:: bash + + php artisan install:api + + .. tip:: + + Skip this step if you are using Laravel 10.x because the file that + the command generates already exists. + Import the controller and add an API route that calls the ``store()`` method in the ``routes/api.php`` file: @@ -42,7 +53,7 @@ Write Data to MongoDB // ... Route::resource('movies', MovieController::class)->only([ - 'store' + 'store' ]); @@ -57,8 +68,8 @@ Write Data to MongoDB class Movie extends Model { - protected $connection = 'mongodb'; - protected $fillable = ['title', 'year', 'runtime', 'imdb', 'plot']; + protected $connection = 'mongodb'; + protected $fillable = ['title', 'year', 'runtime', 'imdb', 'plot']; } .. step:: Post a request to the API From d4e5e2b445d7fd991c43492c375340e3c821771b Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:31:45 -0400 Subject: [PATCH 09/14] DOCSP-36631: laravel feature compatibility (#2802) * DOCSP-36631: laravel feature compatibility * NR PR fixes 1 --- docs/feature-compatibility.txt | 307 +++++++++++++++++++++++++++++++++ docs/index.txt | 1 + docs/query-builder.txt | 2 + 3 files changed, 310 insertions(+) create mode 100644 docs/feature-compatibility.txt diff --git a/docs/feature-compatibility.txt b/docs/feature-compatibility.txt new file mode 100644 index 000000000..b4f0406f3 --- /dev/null +++ b/docs/feature-compatibility.txt @@ -0,0 +1,307 @@ +.. _laravel-feature-compat: + +============================= +Laravel Feature Compatibility +============================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: php framework, odm, support + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +This guide describes the Laravel features that are supported by +the {+odm-long+}. This page discusses Laravel version 11.x feature +availability in {+odm-short+}. + +The following sections contain tables that describe whether individual +features are available in {+odm-short+}. + +Database Features +----------------- + +.. list-table:: + :header-rows: 1 + + * - Eloquent Feature + - Availability + + * - Configuration + - ✓ + + * - Read/Write Connections + - Use :manual:`read preference ` instead. + + * - Multiple Database Connections + - ✓ + + * - Listening for Query Events + - ✓ + + * - Monitoring Cumulative Query Time + - ✓ + + * - Transactions + - ✓ See :ref:`laravel-transactions`. + + * - Command Line Interface (CLI) + - Use the :mdb-shell:`MongoDB Shell <>` (``mongosh``). + + * - Database Inspection + - *Unsupported* + + * - Database Monitoring + - *Unsupported* + +Query Features +-------------- + +The following Eloquent methods are not supported in {+odm-short+}: + +- ``toSql()`` +- ``toRawSql()`` +- ``whereColumn()`` +- ``orWhereColumn()`` +- ``whereFulltext()`` +- ``groupByRaw()`` +- ``orderByRaw()`` +- ``inRandomOrder()`` +- ``union()`` +- ``unionAll()`` +- ``havingRaw()`` +- ``having()`` +- ``havingBetween()`` +- ``orHavingRaw()`` +- ``whereIntegerInRaw()`` +- ``orWhereIntegerInRaw()`` +- ``whereIntegerNotInRaw()`` +- ``orWhereIntegerNotInRaw()`` + +.. list-table:: + :header-rows: 1 + + * - Eloquent Feature + - Availability + + * - Running Queries + - ✓ + + * - Chunking Results + - ✓ + + * - Aggregates + - ✓ + + * - Select Statements + - ✓ + + * - Raw Expressions + - *Unsupported* + + * - Joins + - *Unsupported* + + * - Unions + - *Unsupported* + + * - `Basic Where Clauses `__ + - ✓ + + * - `Additional Where Clauses `__ + - ✓ + + * - Logical Grouping + - ✓ + + * - `Advanced Where Clauses `__ + - ✓ + + * - `Subquery Where Clauses `__ + - *Unsupported* + + * - Ordering + - ✓ + + * - Random Ordering + - *Unsupported* + + * - Grouping + - Partially supported, use :ref:`Aggregation Builders `. + + * - Limit and Offset + - ✓ + + * - Conditional Clauses + - ✓ + + * - Insert Statements + - ✓ + + * - Auto-Incrementing IDs + - *Unsupported as MongoDB uses ObjectIDs* + + * - Upserts + - *Unsupported* + + * - Update Statements + - ✓ + + * - Updating JSON Columns + - *Unsupported* + + * - Increment and Decrement Values + - ✓ + + * - Debugging + - ✓ + +Pagination Features +------------------- + +{+odm-short+} supports all Laravel pagination features. + + +Migration Features +------------------ + +{+odm-short+} supports all Laravel migration features, but the +implementation is specific to MongoDB's schemaless model. + +Seeding Features +---------------- + +{+odm-short+} supports all Laravel seeding features. + +Eloquent Features +----------------- + +.. list-table:: + :header-rows: 1 + + * - Eloquent Feature + - Availability + + * - Models + - ✓ + + * - UUID and ULID Keys + - ✓ + + * - Timestamps + - ✓ + + * - Retrieving Models + - ✓ + + * - Advanced Subqueries + - *Unsupported* + + * - Retrieving or Creating Models + - ✓ + + * - Retrieving Aggregates + - *Partially supported* + + * - Inserting and Updating Models + - ✓ + + * - Upserts + - *Unsupported, but you can use the createOneOrFirst() method* + + * - Deleting Models + - ✓ + + * - Soft Deleting + - ✓ + + * - Pruning Models + - ✓ + +.. tip:: + + To learn more, see the :ref:`laravel-eloquent-model-class` guide. + +Eloquent Relationship Features +------------------------------ + +.. list-table:: + :header-rows: 1 + + * - Eloquent Feature + - Availability + + * - Defining Relationships + - ✓ + + * - Many-to-Many Relationships + - ✓ + + * - Polymorphic Relationships + - ✓ + + * - Dynamic Relationships + - ✓ + + * - Querying Relations + - ✓ + + * - Aggregating Related Models + - *Unsupported* + + * - Inserting and Updating Related Models + - ✓ + +.. tip:: + + To learn more, see the :ref:`laravel-eloquent-model-relationships` guide. + +Eloquent Collection Features +---------------------------- + +{+odm-short+} supports all Eloquent collection features. + +Eloquent Mutator Features +------------------------- + +.. list-table:: + :header-rows: 1 + + * - Eloquent Feature + - Availability + + * - Casts + - ✓ + + * - Array and JSON Casting + - ✓ You can store objects and arrays in MongoDB without serializing to JSON. + + * - Date Casting + - ✓ + + * - Enum Casting + - ✓ + + * - Encrypted Casting + - ✓ + + * - Custom Casts + - ✓ + +.. tip:: + + To learn more, see the :ref:`laravel-eloquent-model-class` guide. + +Eloquent Model Factory Features +------------------------------- + +{+odm-short+} supports all Eloquent factory features. diff --git a/docs/index.txt b/docs/index.txt index ec6825419..af09ee013 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -23,6 +23,7 @@ Laravel MongoDB /queues /transactions /issues-and-help + /feature-compatibility /compatibility /upgrade diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 18f03a2e1..55b5762e4 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -172,6 +172,8 @@ Distinct can be combined with **where**: $spamComments = Comment::where('body', 'like', '%spam%')->get(); +.. _laravel-query-builder-aggregates: + **Aggregation** **Aggregations are only available for MongoDB versions greater than 2.2.x** From e77a4743b1d212f3f103d920b4a55f8ffdc6d466 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Thu, 4 Apr 2024 10:17:51 -0400 Subject: [PATCH 10/14] DOCSP-35970: Find one usage example (#2768) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a usage example page showing how to retrieve one document --------- Co-authored-by: norareidy Co-authored-by: Jérôme Tamarelle --- docs/includes/usage-examples/FindOneTest.php | 36 ++++++++++ docs/usage-examples.txt | 3 +- docs/usage-examples/findOne.txt | 74 ++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 docs/includes/usage-examples/FindOneTest.php create mode 100644 docs/usage-examples/findOne.txt diff --git a/docs/includes/usage-examples/FindOneTest.php b/docs/includes/usage-examples/FindOneTest.php new file mode 100644 index 000000000..98452a6a6 --- /dev/null +++ b/docs/includes/usage-examples/FindOneTest.php @@ -0,0 +1,36 @@ + 'The Shawshank Redemption', 'directors' => ['Frank Darabont', 'Rob Reiner']], + ]); + + // begin-find-one + $movie = Movie::where('directors', 'Rob Reiner') + ->orderBy('_id') + ->first(); + + echo $movie->toJson(); + // end-find-one + + $this->assertInstanceOf(Movie::class, $movie); + $this->expectOutputRegex('/^{"_id":"[a-z0-9]{24}","title":"The Shawshank Redemption","directors":\["Frank Darabont","Rob Reiner"\]}$/'); + } +} diff --git a/docs/usage-examples.txt b/docs/usage-examples.txt index 08dda77ea..2bcd9ac58 100644 --- a/docs/usage-examples.txt +++ b/docs/usage-examples.txt @@ -71,4 +71,5 @@ calls the controller function and returns the result to a web interface. :titlesonly: :maxdepth: 1 - /usage-examples/updateOne \ No newline at end of file + /usage-examples/findOne + /usage-examples/updateOne diff --git a/docs/usage-examples/findOne.txt b/docs/usage-examples/findOne.txt new file mode 100644 index 000000000..39fde3d56 --- /dev/null +++ b/docs/usage-examples/findOne.txt @@ -0,0 +1,74 @@ +.. _laravel-find-one-usage: + +=============== +Find a Document +=============== + +You can retrieve a single document from a collection by calling the ``where()`` and +``first()`` methods on an Eloquent model or a query builder. + +Pass a query filter to the ``where()`` method and then call the ``first()`` method to +return one document in the collection that matches the filter. If multiple documents match +the query filter, ``first()`` returns the first matching document according to the documents' +:term:`natural order` in the database or according to the sort order that you can specify +by using the ``orderBy()`` method. + +Example +------- + +This usage example performs the following actions: + +- Uses the ``Movie`` Eloquent model to represent the ``movies`` collection in the + ``sample_mflix`` database. +- Retrieves a document from the ``movies`` collection that matches a query filter. + +The example calls the following methods on the ``Movie`` model: + +- ``where()``: matches documents in which the value of the ``directors`` field includes ``'Rob Reiner'``. +- ``orderBy()``: sorts matched documents by their ascending ``_id`` values. +- ``first()``: retrieves only the first matching document. + +.. io-code-block:: + + .. input:: ../includes/usage-examples/FindOneTest.php + :start-after: begin-find-one + :end-before: end-find-one + :language: php + :dedent: + + .. output:: + :language: console + :visible: false + + // Result is truncated + + { + "_id": "573a1398f29313caabce94a3", + "plot": "Spinal Tap, one of England's loudest bands, is chronicled by film director + Marty DeBergi on what proves to be a fateful tour.", + "genres": [ + "Comedy", + "Music" + ], + "runtime": 82, + "metacritic": 85, + "rated": "R", + "cast": [ + "Rob Reiner", + "Kimberly Stringer", + "Chazz Dominguez", + "Shari Hall" + ], + "poster": "https://m.media-amazon.com/images/M/MV5BMTQ2MTIzMzg5Nl5BMl5BanBnXkFtZTgwOTc5NDI1MDE@._V1_SY1000_SX677_AL_.jpg", + "title": "This Is Spinal Tap", + ... + } + + +For instructions on editing your Laravel application to run the usage example, see the +:ref:`Usage Example landing page `. + +.. tip:: + + To learn more about retrieving documents with {+odm-short+}, see the + :ref:`laravel-fundamentals-retrieve` guide \ No newline at end of file From e95992502233988e6ffa44e4e9add415cc421ef2 Mon Sep 17 00:00:00 2001 From: Chris Cho Date: Thu, 4 Apr 2024 10:41:19 -0400 Subject: [PATCH 11/14] 040124: Code example syntax fix for Eloquent Model Class page (#2809) * 040124: Code example syntax fix --- docs/eloquent-models/model-class.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/eloquent-models/model-class.txt b/docs/eloquent-models/model-class.txt index 85b7b994b..5542b35ea 100644 --- a/docs/eloquent-models/model-class.txt +++ b/docs/eloquent-models/model-class.txt @@ -249,8 +249,8 @@ The following code example shows mass assignment of the ``Planet`` model: .. code-block:: php $planets = [ - [ 'name' => 'Earth', gravity => 9.8, day_length => '24 hours' ], - [ 'name' => 'Mars', gravity => 3.7, day_length => '25 hours' ], + [ 'name' => 'Earth', 'gravitational_force' => 9.8, 'day_length' => '24 hours' ], + [ 'name' => 'Mars', 'gravitational_force' => 3.7, 'day_length' => '25 hours' ], ]; Planet::create($planets); From 2074da3e2bab6c488da1aef77c61c1081208388b Mon Sep 17 00:00:00 2001 From: stayweek <165480133+stayweek@users.noreply.github.com> Date: Fri, 5 Apr 2024 17:40:13 +0800 Subject: [PATCH 12/14] chore: fix typos in comment (#2806) --- docs/retrieve.txt | 2 +- tests/RelationsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/retrieve.txt b/docs/retrieve.txt index b607d3d4f..1665291e8 100644 --- a/docs/retrieve.txt +++ b/docs/retrieve.txt @@ -393,7 +393,7 @@ documents. Runtime: 95 IMDB Rating: 4 IMDB Votes: 9296 - Plot: A sci-fi update of the famous 6th Century poem. In a beseiged land, Beowulf must + Plot: A sci-fi update of the famous 6th Century poem. In a besieged land, Beowulf must battle against the hideous creature Grendel and his vengeance seeking mother. .. _laravel-retrieve-one: diff --git a/tests/RelationsTest.php b/tests/RelationsTest.php index 8c0a7a4a7..368406feb 100644 --- a/tests/RelationsTest.php +++ b/tests/RelationsTest.php @@ -279,7 +279,7 @@ public function testBelongsToManyAttachesExistingModels(): void $user = User::with('clients')->find($user->_id); - // Assert non attached ID's are detached succesfully + // Assert non attached ID's are detached successfully $this->assertNotContains('1234523', $user->client_ids); // Assert there are two client objects in the relationship From 2b0b5e6e693ae712525f24dafefd3c93d7fbbd40 Mon Sep 17 00:00:00 2001 From: Chris Cho Date: Mon, 8 Apr 2024 09:45:49 -0400 Subject: [PATCH 13/14] DOCSP-35966 query builder (#2790) * DOCSP-35966: Query builder docs standardization --- docs/eloquent-models/schema-builder.txt | 2 + .../query-builder/QueryBuilderTest.php | 579 +++++++ .../query-builder/sample_mflix.movies.json | 196 +++ .../query-builder/sample_mflix.theaters.json | 39 + docs/query-builder.txt | 1400 ++++++++++++----- docs/retrieve.txt | 190 +-- 6 files changed, 1810 insertions(+), 596 deletions(-) create mode 100644 docs/includes/query-builder/QueryBuilderTest.php create mode 100644 docs/includes/query-builder/sample_mflix.movies.json create mode 100644 docs/includes/query-builder/sample_mflix.theaters.json diff --git a/docs/eloquent-models/schema-builder.txt b/docs/eloquent-models/schema-builder.txt index 9fd845b55..39c6a9887 100644 --- a/docs/eloquent-models/schema-builder.txt +++ b/docs/eloquent-models/schema-builder.txt @@ -324,6 +324,8 @@ field: To learn more about these indexes, see :manual:`Index Properties ` in the {+server-docs-name+}. +.. _laravel-eloquent-geospatial-index: + Create a Geospatial Index ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php new file mode 100644 index 000000000..40705102d --- /dev/null +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -0,0 +1,579 @@ +collection('movies') + ->insert(json_decode(file_get_contents(__DIR__ . '/sample_mflix.movies.json'), true)); + } + + protected function importTheaters(): void + { + $db = DB::connection('mongodb'); + + $db->collection('theaters') + ->insert(json_decode(file_get_contents(__DIR__ . '/sample_mflix.theaters.json'), true)); + + $db->collection('theaters') + ->raw() + ->createIndex(['location.geo' => '2dsphere']); + } + + protected function tearDown(): void + { + $db = DB::connection('mongodb'); + $db->collection('movies')->raw()->drop(); + $db->collection('theaters')->raw()->drop(); + + parent::tearDown(); + } + + public function testWhere(): void + { + // begin query where + $result = DB::connection('mongodb') + ->collection('movies') + ->where('imdb.rating', 9.3) + ->get(); + // end query where + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testOrWhere(): void + { + // begin query orWhere + $result = DB::connection('mongodb') + ->collection('movies') + ->where('year', 1955) + ->orWhere('title', 'Back to the Future') + ->get(); + // end query orWhere + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testAndWhere(): void + { + // begin query andWhere + $result = DB::connection('mongodb') + ->collection('movies') + ->where('imdb.rating', '>', 8.5) + ->where('year', '<', 1940) + ->get(); + // end query andWhere + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereNot(): void + { + // begin query whereNot + $result = DB::connection('mongodb') + ->collection('movies') + ->whereNot('imdb.rating', '>', 2) + ->get(); + // end query whereNot + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testNestedLogical(): void + { + // begin query nestedLogical + $result = DB::connection('mongodb') + ->collection('movies') + ->where('imdb.rating', '>', 8.5) + ->where(function (Builder $query) { + return $query + ->where('year', 1986) + ->orWhere('year', 1996); + })->get(); + // end query nestedLogical + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereBetween(): void + { + // begin query whereBetween + $result = DB::connection('mongodb') + ->collection('movies') + ->whereBetween('imdb.rating', [9, 9.5]) + ->get(); + // end query whereBetween + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereNull(): void + { + // begin query whereNull + $result = DB::connection('mongodb') + ->collection('movies') + ->whereNull('runtime') + ->get(); + // end query whereNull + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereIn(): void + { + // begin query whereIn + $result = DB::collection('movies') + ->whereIn('title', ['Toy Story', 'Shrek 2', 'Johnny English']) + ->get(); + // end query whereIn + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereDate(): void + { + // begin query whereDate + $result = DB::connection('mongodb') + ->collection('movies') + ->whereDate('released', '2010-1-15') + ->get(); + // end query whereDate + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testLike(): void + { + // begin query like + $result = DB::collection('movies') + ->where('title', 'like', '%spider_man%') + ->get(); + // end query like + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testDistinct(): void + { + // begin query distinct + $result = DB::collection('movies') + ->distinct('year')->get(); + // end query distinct + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testGroupBy(): void + { + // begin query groupBy + $result = DB::collection('movies') + ->where('rated', 'G') + ->groupBy('runtime') + ->orderBy('runtime', 'asc') + ->get(['title']); + // end query groupBy + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testAggCount(): void + { + // begin aggregation count + $result = DB::collection('movies') + ->count(); + // end aggregation count + + $this->assertIsInt($result); + } + + public function testAggMax(): void + { + // begin aggregation max + $result = DB::collection('movies') + ->max('runtime'); + // end aggregation max + + $this->assertIsInt($result); + } + + public function testAggMin(): void + { + // begin aggregation min + $result = DB::collection('movies') + ->min('year'); + // end aggregation min + + $this->assertIsInt($result); + } + + public function testAggAvg(): void + { + // begin aggregation avg + $result = DB::collection('movies') + ->avg('imdb.rating'); + //->avg('year'); + // end aggregation avg + + $this->assertIsFloat($result); + } + + public function testAggSum(): void + { + // begin aggregation sum + $result = DB::collection('movies') + ->sum('imdb.votes'); + // end aggregation sum + + $this->assertIsInt($result); + } + + public function testAggWithFilter(): void + { + // begin aggregation with filter + $result = DB::collection('movies') + ->where('year', '>', 2000) + ->avg('imdb.rating'); + // end aggregation with filter + + $this->assertIsFloat($result); + } + + public function testOrderBy(): void + { + // begin query orderBy + $result = DB::collection('movies') + ->where('title', 'like', 'back to the future%') + ->orderBy('imdb.rating', 'desc') + ->get(); + // end query orderBy + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testSkip(): void + { + // begin query skip + $result = DB::collection('movies') + ->where('title', 'like', 'star trek%') + ->orderBy('year', 'asc') + ->skip(4) + ->get(); + // end query skip + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testProjection(): void + { + // begin query projection + $result = DB::collection('movies') + ->where('imdb.rating', '>', 8.5) + ->project([ + 'title' => 1, + 'cast' => ['$slice' => [1, 3]], + ]) + ->get(); + // end query projection + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testProjectionWithPagination(): void + { + // begin query projection with pagination + $resultsPerPage = 15; + $projectionFields = ['title', 'runtime', 'imdb.rating']; + + $result = DB::collection('movies') + ->orderBy('imdb.votes', 'desc') + ->paginate($resultsPerPage, $projectionFields); + // end query projection with pagination + + $this->assertInstanceOf(AbstractPaginator::class, $result); + } + + public function testExists(): void + { + // begin query exists + $result = DB::collection('movies') + ->exists('random_review', true); + // end query exists + + $this->assertIsBool($result); + } + + public function testAll(): void + { + // begin query all + $result = DB::collection('movies') + ->where('movies', 'all', ['title', 'rated', 'imdb.rating']) + ->get(); + // end query all + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testSize(): void + { + // begin query size + $result = DB::collection('movies') + ->where('directors', 'size', 5) + ->get(); + // end query size + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testType(): void + { + // begin query type + $result = DB::collection('movies') + ->where('released', 'type', 4) + ->get(); + // end query type + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testMod(): void + { + // begin query modulo + $result = DB::collection('movies') + ->where('year', 'mod', [2, 0]) + ->get(); + // end query modulo + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereRegex(): void + { + // begin query whereRegex + $result = DB::connection('mongodb') + ->collection('movies') + ->where('title', 'REGEX', new Regex('^the lord of .*', 'i')) + ->get(); + // end query whereRegex + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testWhereRaw(): void + { + // begin query raw + $result = DB::collection('movies') + ->whereRaw([ + 'imdb.votes' => ['$gte' => 1000 ], + '$or' => [ + ['imdb.rating' => ['$gt' => 7]], + ['directors' => ['$in' => [ 'Yasujiro Ozu', 'Sofia Coppola', 'Federico Fellini' ]]], + ], + ])->get(); + // end query raw + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testElemMatch(): void + { + // begin query elemMatch + $result = DB::collection('movies') + ->where('writers', 'elemMatch', ['$in' => ['Maya Forbes', 'Eric Roth']]) + ->get(); + // end query elemMatch + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testCursorTimeout(): void + { + // begin query cursor timeout + $result = DB::collection('movies') + ->timeout(2) // value in seconds + ->where('year', 2001) + ->get(); + // end query cursor timeout + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + + public function testNear(): void + { + $this->importTheaters(); + + // begin query near + $results = DB::collection('theaters') + ->where('location.geo', 'near', [ + '$geometry' => [ + 'type' => 'Point', + 'coordinates' => [ + -86.6423, + 33.6054, + ], + ], + '$maxDistance' => 50, + ])->get(); + // end query near + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $results); + } + + public function testGeoWithin(): void + { + // begin query geoWithin + $results = DB::collection('theaters') + ->where('location.geo', 'geoWithin', [ + '$geometry' => [ + 'type' => 'Polygon', + 'coordinates' => [ + [ + [-72, 40], + [-74, 41], + [-72, 39], + [-72, 40], + ], + ], + ], + ])->get(); + // end query geoWithin + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $results); + } + + public function testGeoIntersects(): void + { + // begin query geoIntersects + $results = DB::collection('theaters') + ->where('location.geo', 'geoIntersects', [ + '$geometry' => [ + 'type' => 'LineString', + 'coordinates' => [ + [-73.600525, 40.74416], + [-72.600525, 40.74416], + ], + ], + ])->get(); + // end query geoIntersects + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $results); + } + + public function testGeoNear(): void + { + $this->importTheaters(); + + // begin query geoNear + $results = DB::collection('theaters')->raw( + function (Collection $collection) { + return $collection->aggregate([ + [ + '$geoNear' => [ + 'near' => [ + 'type' => 'Point', + 'coordinates' => [-118.34, 34.10], + ], + 'distanceField' => 'dist.calculated', + 'maxDistance' => 500, + 'includeLocs' => 'dist.location', + 'spherical' => true, + ], + ], + ]); + }, + )->toArray(); + // end query geoNear + + $this->assertIsArray($results); + $this->assertSame(8900, $results[0]['theaterId']); + } + + public function testUpsert(): void + { + // begin upsert + $result = DB::collection('movies') + ->where('title', 'Will Hunting') + ->update( + [ + 'plot' => 'An autobiographical movie', + 'year' => 1998, + 'writers' => [ 'Will Hunting' ], + ], + ['upsert' => true], + ); + // end upsert + + $this->assertIsInt($result); + } + + public function testIncrement(): void + { + // begin increment + $result = DB::collection('movies') + ->where('title', 'Field of Dreams') + ->increment('imdb.votes', 3000); + // end increment + + $this->assertIsInt($result); + } + + public function testDecrement(): void + { + // begin decrement + $result = DB::collection('movies') + ->where('title', 'Sharknado') + ->decrement('imdb.rating', 0.2); + // end decrement + + $this->assertIsInt($result); + } + + public function testPush(): void + { + // begin push + $result = DB::collection('movies') + ->where('title', 'Office Space') + ->push('cast', 'Gary Cole'); + // end push + + $this->assertIsInt($result); + } + + public function testPull(): void + { + // begin pull + $result = DB::collection('movies') + ->where('title', 'Iron Man') + ->pull('genres', 'Adventure'); + // end pull + + $this->assertIsInt($result); + } + + public function testUnset(): void + { + // begin unset + $result = DB::collection('movies') + ->where('title', 'Final Accord') + ->unset('tomatoes.viewer'); + // end unset + + $this->assertIsInt($result); + } +} diff --git a/docs/includes/query-builder/sample_mflix.movies.json b/docs/includes/query-builder/sample_mflix.movies.json new file mode 100644 index 000000000..57873754e --- /dev/null +++ b/docs/includes/query-builder/sample_mflix.movies.json @@ -0,0 +1,196 @@ +[ + { + "genres": [ + "Short" + ], + "runtime": 1, + "cast": [ + "Charles Kayser", + "John Ott" + ], + "title": "Blacksmith Scene", + "directors": [ + "William K.L. Dickson" + ], + "rated": "UNRATED", + "year": 1893, + "imdb": { + "rating": 6.2, + "votes": 1189, + "id": 5 + }, + "tomatoes": { + "viewer": { + "rating": 3, + "numReviews": 184, + "meter": 32 + } + } + }, + { + "genres": [ + "Short", + "Western" + ], + "runtime": 11, + "cast": [ + "A.C. Abadie", + "Gilbert M. 'Broncho Billy' Anderson", + "George Barnes", + "Justus D. Barnes" + ], + "title": "The Great Train Robbery", + "directors": [ + "Edwin S. Porter" + ], + "rated": "TV-G", + "year": 1903, + "imdb": { + "rating": 7.4, + "votes": 9847, + "id": 439 + }, + "tomatoes": { + "viewer": { + "rating": 3.7, + "numReviews": 2559, + "meter": 75 + } + } + }, + { + "genres": [ + "Short", + "Drama", + "Fantasy" + ], + "runtime": 14, + "rated": "UNRATED", + "cast": [ + "Martin Fuller", + "Mrs. William Bechtel", + "Walter Edwin", + "Ethel Jewett" + ], + "title": "The Land Beyond the Sunset", + "directors": [ + "Harold M. Shaw" + ], + "writers": [ + "Dorothy G. Shore" + ], + "year": 1912, + "imdb": { + "rating": 7.1, + "votes": 448, + "id": 488 + }, + "tomatoes": { + "viewer": { + "rating": 3.7, + "numReviews": 53, + "meter": 67 + } + } + }, + { + "genres": [ + "Short", + "Drama" + ], + "runtime": 14, + "cast": [ + "Frank Powell", + "Grace Henderson", + "James Kirkwood", + "Linda Arvidson" + ], + "title": "A Corner in Wheat", + "directors": [ + "D.W. Griffith" + ], + "rated": "G", + "year": 1909, + "imdb": { + "rating": 6.6, + "votes": 1375, + "id": 832 + }, + "tomatoes": { + "viewer": { + "rating": 3.6, + "numReviews": 109, + "meter": 73 + } + } + }, + { + "genres": [ + "Animation", + "Short", + "Comedy" + ], + "runtime": 7, + "cast": [ + "Winsor McCay" + ], + "title": "Winsor McCay, the Famous Cartoonist of the N.Y. Herald and His Moving Comics", + "directors": [ + "Winsor McCay", + "J. Stuart Blackton" + ], + "writers": [ + "Winsor McCay (comic strip \"Little Nemo in Slumberland\")", + "Winsor McCay (screenplay)" + ], + "year": 1911, + "imdb": { + "rating": 7.3, + "votes": 1034, + "id": 1737 + }, + "tomatoes": { + "viewer": { + "rating": 3.4, + "numReviews": 89, + "meter": 47 + } + } + }, + { + "genres": [ + "Comedy", + "Fantasy", + "Romance" + ], + "runtime": 118, + "cast": [ + "Meg Ryan", + "Hugh Jackman", + "Liev Schreiber", + "Breckin Meyer" + ], + "title": "Kate & Leopold", + "directors": [ + "James Mangold" + ], + "writers": [ + "Steven Rogers (story)", + "James Mangold (screenplay)", + "Steven Rogers (screenplay)" + ], + "year": 2001, + "imdb": { + "rating": 6.3, + "votes": 59951, + "id": 35423 + }, + "tomatoes": { + "viewer": { + "rating": 3, + "numReviews": 189426, + "meter": 62 + } + } + } +] diff --git a/docs/includes/query-builder/sample_mflix.theaters.json b/docs/includes/query-builder/sample_mflix.theaters.json new file mode 100644 index 000000000..b8d55381c --- /dev/null +++ b/docs/includes/query-builder/sample_mflix.theaters.json @@ -0,0 +1,39 @@ +[ + { + "theaterId": 1000, + "location": { + "address": { + "street1": "340 W Market", + "city": "Bloomington", + "state": "MN", + "zipcode": "55425" + }, + "geo": { + "type": "Point", + "coordinates": [ + -93.24565, + 44.85466 + ] + } + } + }, + { + "theaterId": 8900, + "location": { + "address": { + "street1": "6801 Hollywood Blvd", + "street2": null, + "city": "Hollywood", + "state": "CA", + "zipcode": "90028" + }, + "geo": { + "type": "Point", + "coordinates": [ + -118.340261, + 34.102593 + ] + } + } + } +] diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 55b5762e4..9650df09b 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -9,590 +9,1116 @@ Query Builder :values: tutorial .. meta:: - :keywords: php framework, odm, code example + :keywords: code example, aggregation -The database driver plugs right into the original query builder. +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol -When using MongoDB connections, you will be able to build fluent queries to -perform database operations. +Overview +-------- -For your convenience, there is a ``collection`` alias for ``table`` and -other MongoDB specific operators/operations. +In this guide, you can learn how to use the {+odm-short+} extension of +the Laravel query builder to work with a MongoDB database. The query builder +lets you use a single syntax and fluent interface to write queries for any +supported database. -.. code-block:: php - - $books = DB::collection('books')->get(); - - $hungerGames = - DB::collection('books') - ->where('name', 'Hunger Games') - ->first(); - -If you are familiar with `Eloquent Queries `__, -there is the same functionality. - -Available operations --------------------- - -**Retrieving all models** - -.. code-block:: php - - $users = User::all(); - -**Retrieving a record by primary key** - -.. code-block:: php - - $user = User::find('517c43667db388101e00000f'); - -**Where** - -.. code-block:: php - - $posts = - Post::where('author.name', 'John') - ->take(10) - ->get(); - -**OR Statements** - -.. code-block:: php - - $posts = - Post::where('votes', '>', 0) - ->orWhere('is_approved', true) - ->get(); - -**AND statements** - -.. code-block:: php - - $users = - User::where('age', '>', 18) - ->where('name', '!=', 'John') - ->get(); - -**NOT statements** - -.. code-block:: php - - $users = User::whereNot('age', '>', 18)->get(); - -**whereIn** - -.. code-block:: php +.. note:: - $users = User::whereIn('age', [16, 18, 20])->get(); + {+odm-short+} extends Laravel's query builder and Eloquent ORM, which can + run similar database operations. To learn more about retrieving documents + by using Eloquent models, see :ref:`laravel-fundamentals-retrieve`. -When using ``whereNotIn`` objects will be returned if the field is -non-existent. Combine with ``whereNotNull('age')`` to omit those documents. +Laravel provides a **facade** to access the query builder class ``DB``, which +lets you perform database operations. Facades, which are static interfaces to +classes, make the syntax more concise, avoid runtime errors, and improve +testability. -**whereBetween** +{+odm-short+} aliases the ``DB`` method ``table()`` as the ``collection()`` +method. Chain methods to specify commands and any constraints. Then, chain +the ``get()`` method at the end to run the methods and retrieve the results. +The following example shows the syntax of a query builder call: .. code-block:: php - $posts = Post::whereBetween('votes', [1, 100])->get(); + DB::collection('') + // chain methods by using the "->" object operator + ->get(); -**whereNull** +This guide provides examples of the following types of query builder operations: + +- :ref:`laravel-retrieve-query-builder` +- :ref:`laravel-modify-results-query-builder` +- :ref:`laravel-mongodb-read-query-builder` +- :ref:`laravel-mongodb-write-query-builder` + +Before You Get Started +---------------------- + +To run the code examples in this guide, complete the +:ref:`Quick Start ` tutorial to configure a web +application, load sample datasets into your MongoDB deployment, and +run the example code from a controller method. + +To perform read and write operations by using the query builder, import the +``Illuminate\Support\Facades\DB`` facade and compose your query. + +.. _laravel-retrieve-query-builder: + +Retrieve Matching Documents +--------------------------- + +This section includes query builder examples for read operations in the +following operator categories: + +- :ref:`Where method ` +- :ref:`Logical conditionals ` +- :ref:`Ranges and type checks ` +- :ref:`Pattern searches ` +- :ref:`Retrieve distinct values ` +- :ref:`Aggregations ` + +.. _laravel-query-builder-where-example: + +Where Method Example +~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to use the ``where()`` query +builder method to retrieve documents from the ``movies`` collection +that contain an ``imdb.rating`` field value of exactly ``9.3``. Click the +:guilabel:`{+code-output-label+}` button to see the results returned +by the query: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query where + :end-before: end query where + + .. output:: + :language: json + :visible: false + + [ + { "title": "Cosmos", + "year": 1980, + "runtime": 60, + "imdb": { + "rating": 9.3, + "votes": 17174, + "id": 81846 + }, + "plot": "Astronomer Carl Sagan leads us on an engaging guided tour of the various elements and cosmological theories of the universe.", + ... + }, + { "title": "The Shawshank Redemption", + "year": 1994, + "runtime": 142, + "imdb": { + "rating": 9.3, + "votes": 1521105, + "id": 111161 + }, + "plot": "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", + ... + }, + { "title": "The Real Miyagi", + "year": 2015, + "runtime": 90, + "imdb": { + "rating": 9.3, + "votes": 41, + "id": 2313306 + }, + "plot": "The life of the greatest karate master of a generation.", + ... + } + ] + +.. _laravel-query-builder-logical-operations: + +Logical Conditional Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The examples in this section show the query builder syntax you +can use to perform the following logical conditional operations: + +- :ref:`Logical OR to match one or more conditions ` +- :ref:`Logical AND to match all conditions ` +- :ref:`Logical NOT to match the negation of the condition ` +- :ref:`Nested logical operator groups ` + +.. _laravel-query-builder-logical-or: + +Logical OR Example +^^^^^^^^^^^^^^^^^^ + +The following example shows how to chain the ``orWhere()`` +query builder method to retrieve documents from the +``movies`` collection that either match the ``year`` +value of ``1955`` or match the ``title`` value ``"Back to the Future"``: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query orWhere + :end-before: end query orWhere + +.. _laravel-query-builder-logical-and: + +Logical AND Example +^^^^^^^^^^^^^^^^^^^ + +The following example shows how to chain the ``where()`` +query builder method to retrieve documents from the +``movies`` collection that match both an ``imdb.rating`` +value greater than ``8.5`` and a ``year`` value of less than +``1940``: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query andWhere + :end-before: end query andWhere + +.. _laravel-query-builder-logical-not: + +Logical NOT Example +^^^^^^^^^^^^^^^^^^^ + +The following example shows how to call the ``whereNot()`` +query builder method to retrieve documents from the +``movies`` collection that match documents that do not have an ``imdb.rating`` +value greater than ``2``. This is equivalent to matching all documents +that have an ``imdb.rating`` of less than or equal to ``2``: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereNot + :end-before: end query whereNot + +.. _laravel-query-builder-logical-nested: + +Nested Logical Operator Group Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to chain the ``where()`` +query builder method to retrieve documents from the +``movies`` collection that match both of the following +conditions. This example passes a closure as the first +parameter of the ``where()`` query builder method to group +the logical OR group: + +- ``imdb.rating`` value is greater than ``8.5`` +- ``year`` value is either ``1986`` or ``1996`` + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query nestedLogical + :end-before: end query nestedLogical + +.. _laravel-query-builder-range-type: + +Ranges and Type Checks +~~~~~~~~~~~~~~~~~~~~~~ + +The examples in this section show the query builder syntax you can use to +match values by using the following range queries and type check operations: + +- :ref:`Values within a numerical range ` +- :ref:`Null or missing values ` +- :ref:`One or more values of a set ` +- :ref:`Match dates ` +- :ref:`Match a text pattern ` -.. code-block:: php +.. _laravel-query-builder-wherebetween: - $users = User::whereNull('age')->get(); +Numerical Range Example +^^^^^^^^^^^^^^^^^^^^^^^ -**whereDate** +The following example shows how to use the ``whereBetween()`` +query builder method to retrieve documents from the +``movies`` collection that contain an ``imdb.rating`` value +between ``9`` and ``9.5``: -.. code-block:: php +.. io-code-block:: + :copyable: true - $users = User::whereDate('birthday', '2021-5-12')->get(); + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereBetween + :end-before: end query whereBetween + + .. output:: + :language: none + :visible: false + + [ + { "title" "The Godfather", "imdb": { "rating": 9.2, "votes": 1038358, "id": 68646 }, ... }, + { "title": "Hollywood", "imdb": { "rating": 9.1, "votes": 511,"id": 80230 }, ... }, + { "title": "Cosmos", "imdb": { "rating": 9.3, "votes": 17174, "id": 81846 }, ... }, + ... + ] -The usage is the same as ``whereMonth`` / ``whereDay`` / ``whereYear`` / ``whereTime`` +.. _laravel-query-builder-null: + +Null or Missing Values Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``whereNull()`` +query builder method to retrieve documents from the +``movies`` collection that omit a ``runtime`` value +or field: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereNull + :end-before: end query whereNull -**Advanced wheres** +.. _laravel-query-builder-wherein: -.. code-block:: php +One or More Values of a Set Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``whereIn()`` +query builder method to retrieve documents from the +``movies`` collection that match at least one of the +``title`` values in the specified set: + +.. io-code-block:: + :copyable: true - $users = - User::where('name', 'John') - ->orWhere(function ($query) { - return $query - ->where('votes', '>', 100) - ->where('title', '<>', 'Admin'); - })->get(); + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereIn + :end-before: end query whereIn -**orderBy** + .. output:: + :language: json + :visible: false -.. code-block:: php + [ + { "title": "Toy Story", "year": 1995, "runtime": 81, ... }, + { "title": "Johnny English", "year": 2003, "runtime": 87, ... }, + { "title": "Shrek 2", "year" 2004, "runtime": 93, ... }, + ... + ] - $users = User::orderBy('age', 'desc')->get(); +.. _laravel-query-builder-wheredate: -**Offset & Limit (skip & take)** +Match Dates Example +^^^^^^^^^^^^^^^^^^^ -.. code-block:: php +The following example shows how to use the ``whereDate()`` +query builder method to retrieve documents from the +``movies`` collection that match the specified date of +``2010-1-15`` in the ``released`` field: - $users = - User::skip(10) - ->take(5) - ->get(); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereDate + :end-before: end query whereDate + +.. _laravel-query-builder-pattern: -**groupBy** +Text Pattern Match Example +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Selected columns that are not grouped will be aggregated with the ``$last`` -function. +The following example shows how to use the ``like`` query operator +with the ``where()`` query builder method to retrieve documents from the +``movies`` collection by using a specified text pattern. + +Text patterns can contain text mixed with the following +wildcard characters: + +- ``%`` which matches zero or more characters +- ``_`` which matches a single character + +.. io-code-block:: + :copyable: true + + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query like + :end-before: end query like + + .. output:: + :language: json + :visible: false + + [ + { "title": "Kiss of the Spider Woman", ... }, + { "title": "Spider-Man", ... }, + { "title": "Spider-Man 2", ...}, + ... + ] + +.. _laravel-query-builder-distinct: + +Retrieve Distinct Values +~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to use the ``distinct()`` +query builder method to retrieve all the different values +of the ``year`` field for documents in the ``movies`` collections. + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query distinct + :end-before: end query distinct + +.. _laravel-query-builder-aggregations: + +Aggregations +~~~~~~~~~~~~ + +The examples in this section show the query builder syntax you +can use to perform **aggregations**. Aggregations are operations +that compute values from a set of query result data. You can use +aggregations to compute and return the following information: + +- :ref:`Results grouped by common field values ` +- :ref:`Count the number of results ` +- :ref:`Maximum value of a field ` +- :ref:`Minimum value of a field ` +- :ref:`Average value of a field ` +- :ref:`Summed value of a field ` +- :ref:`Aggregate matched results ` + +.. _laravel-query-builder-aggregation-groupby: + +Results Grouped by Common Field Values Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``groupBy()`` query builder method +to retrieve document data grouped by shared values of the ``runtime`` field. +This example chains the following operations to match documents from the +``movies`` collection that contain a ``rated`` value of ``G`` and include the +``title`` field of one movie for each distinct ``runtime`` value: + +- Match only documents that contain a ``rated`` field value of ``"G"`` by + using the ``where()`` method +- Group data by the distinct values of the ``runtime`` field, which is + assigned the ``_id`` field, by using the ``groupBy()`` method +- Sort the groups by the ``runtime`` field by using the ``orderBy()`` method +- Return ``title`` data from the last document in the grouped result by + specifying it in the ``get()`` method + +.. tip:: + + The ``groupBy()`` method calls the MongoDB ``$group`` aggregation operator + and ``$last`` accumulator operator. To learn more about these operators, see + :manual:`$group (aggregation) ` + in the {+server-docs-name+}. + +.. io-code-block:: + :copyable: true + + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query groupBy + :end-before: end query groupBy + + .. output:: + :language: json + :visible: false + + [ + ... + { + "_id": { "runtime": 64 }, + "runtime": 64, + "title": "Stitch! The Movie" + }, + { + "_id": { "runtime": 67 }, + "runtime": 67, + "title": "Bartok the Magnificent" + }, + { + "_id": { "runtime":68 }, + "runtime": 68, + "title": "Mickey's Twice Upon a Christmas" + }, + ... + ] + +.. _laravel-query-builder-aggregation-count: + +Number of Results Example +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``count()`` +query builder method to return the number of documents +contained in the ``movies`` collection: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation count + :end-before: end aggregation count + +.. _laravel-query-builder-aggregation-max: + +Maximum Value of a Field Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``max()`` +query builder method to return the highest numerical +value of the ``runtime`` field from the entire +``movies`` collection: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation max + :end-before: end aggregation max + +.. _laravel-query-builder-aggregation-min: + +Minimum Value of a Field Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``min()`` +query builder method to return the lowest numerical +value of the ``year`` field from the entire ``movies`` +collection: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation min + :end-before: end aggregation min + +.. _laravel-query-builder-aggregation-avg: + +Average Value of a Field Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``avg()`` +query builder method to return the numerical average, or +arithmetic mean, of the ``imdb.rating`` values from +the entire ``movies`` collection. + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation avg + :end-before: end aggregation avg + +.. _laravel-query-builder-aggregation-sum: + +Summed Value of a Field Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to use the ``sum()`` +query builder method to return the numerical total of +the ``imdb.votes`` values from the entire ``movies`` +collection: + + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation sum + :end-before: end aggregation sum + +.. _laravel-query-builder-aggregate-matched: + +Aggregate Matched Results Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example shows how to aggregate data +from results that match a query. The query matches all +movies after the year ``2000`` and computes the average +value of ``imdb.rating`` of those matches by using the +``avg()`` method: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin aggregation with filter + :end-before: end aggregation with filter - $users = - Users::groupBy('title') - ->get(['title', 'name']); +.. _laravel-modify-results-query-builder: -**Distinct** +Modify Query Results +-------------------- -Distinct requires a field for which to return the distinct values. +This section includes query builder examples for the +following functions that modify the order and format +of query results: + +- :ref:`Order results by the value of a field ` +- :ref:`Omit a specified number of results ` +- :ref:`Show a subset of fields and array values in the results ` +- :ref:`Paginate the results ` + +.. _laravel-query-builder-orderby: + +Order Results Example +~~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to use the ``orderBy()`` +query builder method to arrange the results that match +the filter specified in the ``title`` field by the +``imdb.rating`` value in descending order: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query orderBy + :end-before: end query orderBy + + .. output:: + :language: json + :visible: false + + [ + { "title": "Back to the Future", "imdb": { "rating":8.5,"votes":636511,"id":88763 }, ... }, + { "title": "Back to the Future Part II", "imdb": { "rating":7.8,"votes":292539,"id":96874 }, ... }, + { "title": "Back to the Future Part III", "imdb": {"rating":7.4,"votes":242390,"id":99088 }, ... }, + ... + ] + +.. _laravel-query-builder-skip: + +Omit a Specified Number of Results Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to use the ``skip()`` query builder method to +omit the first four results that match the filter specified in the ``title`` +field, sorted by the ``year`` value in ascending order: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query skip + :end-before: end query skip + +.. _laravel-query-builder-project: + +Show a Subset of Fields and Array Values in the Results Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to use the ``project()`` +query builder method to match documents that contain an +``imdb.rating`` value higher than ``8.5`` and return +only the following field values: + +- Title of the movie in the ``title`` +- Second through fourth values of the ``cast`` array field, if they exist +- Document ``_id`` field, which is automatically included + +.. io-code-block:: + :copyable: true + + .. input:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query projection + :end-before: end query projection + + .. output:: + :language: json + :visible: false + + [ + { + "_id": { ... }, + "title": "City Lights" + "cast": [ + "Florence Lee", + "Harry Myers", + "Al Ernest Garcia" + ], + }, + { + "_id": { ... }, + "title": "Modern Times", + "cast": [ + "Paulette Goddard", + "Henry Bergman", + "Tiny Sandford" + ] + }, + { + "_id": { ... }, + "title": "Casablanca" + "cast": [ + "Ingrid Bergman", + "Paul Henreid", + "Claude Rains" + ], + }, + ... + ] + +.. _laravel-query-builder-paginate: + +Paginate the Results Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: php +The following example shows how to use the ``paginate()`` query builder method +to divide the entire ``movie`` collection into discrete result sets of 15 +documents. The example also includes a sort order to arrange the results by +the ``imdb.votes`` field in descending order and a projection that includes +only specific fields in the results. - $users = User::distinct()->get(['name']); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query projection with pagination + :end-before: end query projection with pagination - // Equivalent to: - $users = User::distinct('name')->get(); +To learn more about pagination, see +`Paginating Query Builder Results `__ +in the Laravel documentation. -Distinct can be combined with **where**: +.. _laravel-mongodb-read-query-builder: -.. code-block:: php +Retrieve Data by Using MongoDB Operations +----------------------------------------- - $users = - User::where('active', true) - ->distinct('name') - ->get(); +This section includes query builder examples that show how +to use the following MongoDB-specific query operations: -**Like** +- :ref:`Match documents that contain a field ` +- :ref:`Match documents that contain all specified fields ` +- :ref:`Match documents that contain a specific number of elements in an array ` +- :ref:`Match documents that contain a particular data type in a field ` +- :ref:`Match documents that contain a computed modulo value ` +- :ref:`Match documents that match a regular expression ` +- :ref:`Run MongoDB Query API operations ` +- :ref:`Match documents that contain array elements ` +- :ref:`Specify a cursor timeout ` +- :ref:`Match locations by using geospatial searches ` -.. code-block:: php +.. _laravel-query-builder-exists: - $spamComments = Comment::where('body', 'like', '%spam%')->get(); +Contains a Field Example +~~~~~~~~~~~~~~~~~~~~~~~~ -.. _laravel-query-builder-aggregates: +The following example shows how to use the ``exists()`` +query builder method to match documents that contain the +field ``random_review``: -**Aggregation** +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query exists + :end-before: end query exists -**Aggregations are only available for MongoDB versions greater than 2.2.x** +To learn more about this query operator, see +:manual:`$exists ` +in the {+server-docs-name+}. -.. code-block:: php +.. _laravel-query-builder-all: - $total = Product::count(); - $price = Product::max('price'); - $price = Product::min('price'); - $price = Product::avg('price'); - $total = Product::sum('price'); - -Aggregations can be combined with **where**: +Contains All Fields Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: php +The following example shows how to use the ``all`` query +operator with the ``where()`` query builder method to match +documents that contain all the specified fields: - $sold = Orders::where('sold', true)->sum('price'); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query all + :end-before: end query all -Aggregations can be also used on sub-documents: +To learn more about this query operator, see +:manual:`$all ` +in the {+server-docs-name+}. -.. code-block:: php +.. _laravel-query-builder-size: - $total = Order::max('suborder.price'); +Match Array Size Example +~~~~~~~~~~~~~~~~~~~~~~~~ -.. note:: +The following example shows how to pass the ``size`` +query operator with the ``where()`` query builder +method to match documents that contain a ``directors`` +field that contains an array of exactly five elements: - This aggregation only works with single sub-documents (like ``EmbedsOne``) - not subdocument arrays (like ``EmbedsMany``). +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query size + :end-before: end query size -**Incrementing/Decrementing the value of a column** +To learn more about this query operator, see +:manual:`$size ` +in the {+server-docs-name+}. -Perform increments or decrements (default 1) on specified attributes: +.. _laravel-query-builder-type: -.. code-block:: php +Match Data Type Example +~~~~~~~~~~~~~~~~~~~~~~~ - Cat::where('name', 'Kitty')->increment('age'); +The following example shows how to pass the ``type`` +query operator with the ``where()`` query builder +method to match documents that contain a type ``4`` value, +which corresponds to an array data type, in the +``released`` field. - Car::where('name', 'Toyota')->decrement('weight', 50); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query type + :end-before: end query type -The number of updated objects is returned: +To learn more about the type codes and query operator, see +:manual:`$type ` +in the {+server-docs-name+}. -.. code-block:: php +.. _laravel-query-builder-mod: - $count = User::increment('age'); +Match a Value Computed with Modulo Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You may also specify more columns to update: +The following example shows how to pass the ``mod`` +query operator with the ``where()`` query builder +method to match documents by using the expression +``year % 2 == 0``, which matches even values for +the ``year`` field: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query modulo + :end-before: end query modulo - Cat::where('age', 3) - ->increment('age', 1, ['group' => 'Kitty Club']); +To learn more about this query operator, see +:manual:`$mod ` +in the {+server-docs-name+}. - Car::where('weight', 300) - ->decrement('weight', 100, ['latest_change' => 'carbon fiber']); +.. _laravel-query-builder-regex: -MongoDB-specific operators +Match a Regular Expression ~~~~~~~~~~~~~~~~~~~~~~~~~~ -In addition to the Laravel Eloquent operators, all available MongoDB query -operators can be used with ``where``: +The following example shows how to pass the ``REGEX`` +query operator with the ``where()`` query builder +method to match documents that contain a ``title`` +field that matches the specified regular expression: -.. code-block:: php - - User::where($fieldName, $operator, $value)->get(); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereRegex + :end-before: end query whereRegex -It generates the following MongoDB filter: +To learn more about regular expression queries in MongoDB, see +:manual:`$regex ` +in the {+server-docs-name+}. -.. code-block:: ts +.. _laravel-query-builder-whereRaw: - { $fieldName: { $operator: $value } } +Run MongoDB Query API Operations Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Exists** +The following example shows how to use the ``whereRaw()`` +query builder method to run a query operation written by +using the MongoDB Query API syntax: -Matches documents that have the specified field. - -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query raw + :end-before: end query raw - User::where('age', 'exists', true)->get(); +The following code shows the equivalent MongoDB Query API syntax: -**All** - -Matches arrays that contain all elements specified in the query. +.. code-block:: -.. code-block:: php + db.movies.find({ + "imdb.votes": { $gte: 1000 }, + $or: [{ + imdb.rating: { $gt: 7 }, + directors: { $in: [ "Yasujiro Ozu", "Sofia Coppola", "Federico Fellini" ] } + }]}); - User::where('roles', 'all', ['moderator', 'author'])->get(); +To learn more about the MongoDB Query API, see +:manual:`MongoDB Query API ` in the {+server-docs-name+}. -**Size** +.. _laravel-query-builder-elemMatch: -Selects documents if the array field is a specified size. +Match Array Elements Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: php +The following example shows how to pass the ``elemMatch`` +query operator with the ``where()`` query builder +method to match documents that contain an array element +that matches at least one of the conditions in the +specified query: - Post::where('tags', 'size', 3)->get(); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query elemMatch + :end-before: end query elemMatch -**Regex** +To learn more about regular expression queries in MongoDB, see +the :manual:`$elemMatch operator ` +in the {+server-docs-name+}. -Selects documents where values match a specified regular expression. +.. _laravel-query-builder-cursor-timeout: -.. code-block:: php +Specify Cursor Timeout Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - use MongoDB\BSON\Regex; +The following example shows how to use the ``timeout()`` method +to specify a maximum duration to wait for cursor operations to complete. - User::where('name', 'regex', new Regex('.*doe', 'i'))->get(); +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query cursor timeout + :end-before: end query cursor timeout .. note:: - You can also use the Laravel regexp operations. These will automatically - convert your regular expression string to a ``MongoDB\BSON\Regex`` object. - -.. code-block:: php - - User::where('name', 'regexp', '/.*doe/i')->get(); - -The inverse of regexp: - -.. code-block:: php - - User::where('name', 'not regexp', '/.*doe/i')->get(); + This setting specifies a ``maxTimeMS`` value in seconds instead of + milliseconds. To learn more about the ``maxTimeMS`` value, see + `MongoDB\Collection::find() `__ + in the PHP Library documentation. -**ElemMatch** +.. _laravel-query-builder-geospatial: -The :manual:`$elemMatch ` operator -matches documents that contain an array field with at least one element that -matches all the specified query criteria. +Match Locations by Using Geospatial Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following query matches only those documents where the results array -contains at least one element that is both greater than or equal to 80 and -is less than 85: +The examples in this section show the query builder syntax you +can use to perform geospatial queries on GeoJSON or coordinate +pair data to retrieve the following types of locations: -.. code-block:: php +- :ref:`Near a position ` +- :ref:`Within a the boundary of a GeoJSON object ` +- :ref:`Intersecting a GeoJSON object ` +- :ref:`Proximity data for nearby matches ` - User::where('results', 'elemMatch', ['gte' => 80, 'lt' => 85])->get(); +.. important:: -A closure can be used to create more complex sub-queries. + To perform GeoJSON queries in MongoDB, you must create either ``2d`` or + ``2dsphere`` index on the collection. To learn how to create geospatial + indexes, see the :ref:`laravel-eloquent-geospatial-index` section in the + Schema Builder guide. -The following query matches only those documents where the results array -contains at least one element with both product equal to "xyz" and score -greater than or equal to 8: +To learn more about GeoJSON objects that MongoDB supports, +see :manual:`GeoJSON Objects ` +in the {+server-docs-name+}. -.. code-block:: php +.. _laravel-query-builder-geospatial-near: - User::where('results', 'elemMatch', function (Builder $builder) { - $builder - ->where('product', 'xyz') - ->andWhere('score', '>', 50); - })->get(); +Near a Position Example +~~~~~~~~~~~~~~~~~~~~~~~ -**Type** +The following example shows how to use the ``near`` query operator +with the ``where()`` query builder method to match documents that +contain a location that is up to ``50`` meters from a GeoJSON Point +object: -Selects documents if a field is of the specified type. For more information -check: :manual:`$type ` in the -MongoDB Server documentation. +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query near + :end-before: end query near -.. code-block:: php +To learn more about this operator, see +:manual:`$near operator ` +in the {+server-docs-name+}. - User::where('age', 'type', 2)->get(); +.. _laravel-query-builder-geospatial-geoWithin: -**Mod** +Within an Area Example +~~~~~~~~~~~~~~~~~~~~~~ -Performs a modulo operation on the value of a field and selects documents with -a specified result. +The following example shows how to use the ``geoWithin`` +query operator with the ``where()`` +query builder method to match documents that contain a +location within the bounds of the specified ``Polygon`` +GeoJSON object: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query geoWithin + :end-before: end query geoWithin - User::where('age', 'mod', [10, 0])->get(); +.. _laravel-query-builder-geospatial-geoIntersects: -MongoDB-specific Geo operations +Intersecting a Geometry Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Near** - -.. code-block:: php - - $bars = Bar::where('location', 'near', [ - '$geometry' => [ - 'type' => 'Point', - 'coordinates' => [ - -0.1367563, // longitude - 51.5100913, // latitude - ], - ], - '$maxDistance' => 50, - ])->get(); - -**GeoWithin** - -.. code-block:: php - - $bars = Bar::where('location', 'geoWithin', [ - '$geometry' => [ - 'type' => 'Polygon', - 'coordinates' => [ - [ - [-0.1450383, 51.5069158], - [-0.1367563, 51.5100913], - [-0.1270247, 51.5013233], - [-0.1450383, 51.5069158], - ], - ], - ], - ])->get(); - -**GeoIntersects** - -.. code-block:: php - - $bars = Bar::where('location', 'geoIntersects', [ - '$geometry' => [ - 'type' => 'LineString', - 'coordinates' => [ - [-0.144044, 51.515215], - [-0.129545, 51.507864], - ], - ], - ])->get(); - -**GeoNear** - -You can make a ``geoNear`` query on MongoDB. -You can omit specifying the automatic fields on the model. -The returned instance is a collection, so you can call the `Collection `__ operations. -Make sure that your model has a ``location`` field, and a -`2ndSphereIndex `__. -The data in the ``location`` field must be saved as `GeoJSON `__. -The ``location`` points must be saved as `WGS84 `__ -reference system for geometry calculation. That means that you must -save ``longitude and latitude``, in that order specifically, and to find near -with calculated distance, you ``must do the same way``. +The following example shows how to use the ``geoInstersects`` +query operator with the ``where()`` query builder method to +match documents that contain a location that intersects with +the specified ``LineString`` GeoJSON object: -.. code-block:: +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query geoIntersects + :end-before: end query geoIntersects - Bar::find("63a0cd574d08564f330ceae2")->update( - [ - 'location' => [ - 'type' => 'Point', - 'coordinates' => [ - -0.1367563, - 51.5100913 - ] - ] - ] - ); - $bars = Bar::raw(function ($collection) { - return $collection->aggregate([ - [ - '$geoNear' => [ - "near" => [ "type" => "Point", "coordinates" => [-0.132239, 51.511874] ], - "distanceField" => "dist.calculated", - "minDistance" => 0, - "maxDistance" => 6000, - "includeLocs" => "dist.location", - "spherical" => true, - ] - ] - ]); - }); - -Inserts, updates and deletes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _laravel-query-builder-geospatial-geoNear: -Inserting, updating and deleting records works just like the original Eloquent. -Please check `Laravel Docs' Eloquent section `__. +Proximity Data for Nearby Matches Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Here, only the MongoDB-specific operations are specified. +The following example shows how to use the ``geoNear`` aggregation operator +with the ``raw()`` query builder method to perform an aggregation that returns +metadata, such as proximity information for each match: -MongoDB specific operations -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query geoNear + :end-before: end query geoNear -**Raw Expressions** +To learn more about this aggregation operator, see +:manual:`$geoNear operator ` +in the {+server-docs-name+}. -These expressions will be injected directly into the query. +.. _laravel-mongodb-write-query-builder: -.. code-block:: php +Write Data by Using MongoDB Write Operations +-------------------------------------------- - User::whereRaw([ - 'age' => ['$gt' => 30, '$lt' => 40], - ])->get(); +This section includes query builder examples that show how to use the +following MongoDB-specific write operations: - User::whereRaw([ - '$where' => '/.*123.*/.test(this.field)', - ])->get(); +- :ref:`Upsert a document ` +- :ref:`Increment a numerical value ` +- :ref:`Decrement a numerical value ` +- :ref:`Add an array element ` +- :ref:`Remove a value from an array ` +- :ref:`Remove a field from a document ` - User::whereRaw([ - '$where' => '/.*123.*/.test(this["hyphenated-field"])', - ])->get(); +.. _laravel-mongodb-query-builder-upsert: -You can also perform raw expressions on the internal MongoCollection object. -If this is executed on the model class, it will return a collection of models. +Upsert a Document Example +~~~~~~~~~~~~~~~~~~~~~~~~~ -If this is executed on the query builder, it will return the original response. +The following example shows how to use the ``update()`` query builder method +and ``upsert`` option to update the matching document or insert one with the +specified data if it does not exist. When you set the ``upsert`` option to +``true`` and the document does not exist, the command inserts both the data +and the ``title`` field and value specified in the ``where()`` query operation: -**Cursor timeout** +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin upsert + :end-before: end upsert -To prevent ``MongoCursorTimeout`` exceptions, you can manually set a timeout -value that will be applied to the cursor: +The ``update()`` query builder method returns the number of documents that the +operation updated or inserted. -.. code-block:: php +.. _laravel-mongodb-query-builder-increment: - DB::collection('users')->timeout(-1)->get(); +Increment a Numerical Value Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Upsert** +The following example shows how to use the ``increment()`` +query builder method to add ``3000`` to the value of +the ``imdb.votes`` field in the matched document: -Update or insert a document. Other options for the update method can be -passed directly to the native update method. +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin increment + :end-before: end increment -.. code-block:: php +The ``increment()`` query builder method returns the number of documents that the +operation updated. - // Query Builder - DB::collection('users') - ->where('name', 'John') - ->update($data, ['upsert' => true]); +.. _laravel-mongodb-query-builder-decrement: - // Eloquent - $user->update($data, ['upsert' => true]); +Decrement a Numerical Value Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Projections** +The following example shows how to use the ``decrement()`` query builder +method to subtract ``0.2`` from the value of the ``imdb.rating`` field in the +matched document: -You can apply projections to your queries using the ``project`` method. +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin decrement + :end-before: end decrement -.. code-block:: php +The ``decrement()`` query builder method returns the number of documents that the +operation updated. - DB::collection('items') - ->project(['tags' => ['$slice' => 1]]) - ->get(); +.. _laravel-mongodb-query-builder-push: - DB::collection('items') - ->project(['tags' => ['$slice' => [3, 7]]]) - ->get(); - -**Projections with Pagination** - -.. code-block:: php - - $limit = 25; - $projections = ['id', 'name']; - - DB::collection('items') - ->paginate($limit, $projections); - -**Push** - -Add items to an array. - -.. code-block:: php - - DB::collection('users') - ->where('name', 'John') - ->push('items', 'boots'); - - $user->push('items', 'boots'); - -.. code-block:: php - - DB::collection('users') - ->where('name', 'John') - ->push('messages', [ - 'from' => 'Jane Doe', - 'message' => 'Hi John', - ]); - - $user->push('messages', [ - 'from' => 'Jane Doe', - 'message' => 'Hi John', - ]); - -If you **DON'T** want duplicate items, set the third parameter to ``true``: - -.. code-block:: php - - DB::collection('users') - ->where('name', 'John') - ->push('items', 'boots', true); - - $user->push('items', 'boots', true); - -**Pull** - -Remove an item from an array. - -.. code-block:: php - - DB::collection('users') - ->where('name', 'John') - ->pull('items', 'boots'); +Add an Array Element Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - $user->pull('items', 'boots'); +The following example shows how to use the ``push()`` query builder method to +add ``"Gary Cole"`` to the ``cast`` array field in the matched document: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin push + :end-before: end push - DB::collection('users') - ->where('name', 'John') - ->pull('messages', [ - 'from' => 'Jane Doe', - 'message' => 'Hi John', - ]); +The ``push()`` query builder method returns the number of documents that the +operation updated. - $user->pull('messages', [ - 'from' => 'Jane Doe', - 'message' => 'Hi John', - ]); +.. _laravel-mongodb-query-builder-pull: -**Unset** +Remove an Array Element Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Remove one or more fields from a document. +The following example shows how to use the ``pull()`` query builder method +to remove the ``"Adventure"`` value from the ``genres`` field from the document +matched by the query: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin pull + :end-before: end pull - DB::collection('users') - ->where('name', 'John') - ->unset('note'); +The ``pull()`` query builder method returns the number of documents that the +operation updated. - $user->unset('note'); +.. _laravel-mongodb-query-builder-unset: - $user->save(); +Remove a Field Example +~~~~~~~~~~~~~~~~~~~~~~ -Using the native ``unset`` on models will work as well: +The following example shows how to use the ``unset()`` query builder method +to remove the ``tomatoes.viewer`` field and value from the document matched +by the query: -.. code-block:: php +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin unset + :end-before: end unset - unset($user['note']); - unset($user->node); +The ``unset()`` query builder method returns the number of documents that the +operation updated. diff --git a/docs/retrieve.txt b/docs/retrieve.txt index b607d3d4f..1447d20a0 100644 --- a/docs/retrieve.txt +++ b/docs/retrieve.txt @@ -43,7 +43,7 @@ sample data and creating the following files in your Laravel web application: - ``browse_movies.blade.php`` file, which contains HTML code to display the results of database operations -The following sections describe how to edit the files in your Laravel application to run +The following sections describe how to edit the files in your Laravel application to run the find operation code examples and view the expected output. .. _laravel-retrieve-matching: @@ -51,41 +51,37 @@ the find operation code examples and view the expected output. Retrieve Documents that Match a Query ------------------------------------- -You can retrieve documents that match a set of criteria by passing a query filter to the ``where()`` -method. A query filter specifies field value requirements and instructs the find operation -to only return documents that meet these requirements. To run the query, call the ``where()`` -method on an Eloquent model or query builder that represents your collection. +You can use Laravel's Eloquent object-relational mapper (ORM) to create models +that represent MongoDB collections and chain methods on them to specify +query criteria. -You can use one of the following ``where()`` method calls to build a query: - -- ``where('', )``: builds a query that matches documents in which the - target field has the exact specified value +To retrieve documents that match a set of criteria, pass a query filter to the +``where()`` method. -- ``where('', '', )``: builds a query that matches - documents in which the target field's value meets the comparison criteria +A query filter specifies field value requirements and instructs the find +operation to return only documents that meet these requirements. -After building your query with the ``where()`` method, use the ``get()`` method to -retrieve the query results. +You can use Laravel's Eloquent object-relational mapper (ORM) to create models +that represent MongoDB collections. To retrieve documents from a collection, +call the ``where()`` method on the collection's corresponding Eloquent model. -To apply multiple sets of criteria to the find operation, you can chain a series -of ``where()`` methods together. - -.. tip:: +You can use one of the following ``where()`` method calls to build a query: - To learn more about other query methods in {+odm-short+}, see the :ref:`laravel-query-builder` - page. +- ``where('', )`` builds a query that matches documents in + which the target field has the exact specified value -.. _laravel-retrieve-eloquent: +- ``where('', '', )`` builds a query + that matches documents in which the target field's value meets the comparison + criteria -Use Eloquent Models to Retrieve Documents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To apply multiple sets of criteria to the find operation, you can chain a series +of ``where()`` methods together. -You can use Laravel's Eloquent object-relational mapper (ORM) to create models that represent -MongoDB collections. To retrieve documents from a collection, call the ``where()`` method -on the collection's corresponding Eloquent model. +After building your query with the ``where()`` method, chain the ``get()`` +method to retrieve the query results. -This example calls two ``where()`` methods on the ``Movie`` Eloquent model to retrieve -documents that meet the following criteria: +This example calls two ``where()`` methods on the ``Movie`` Eloquent model to +retrieve documents that meet the following criteria: - ``year`` field has a value of ``2010`` - ``imdb.rating`` nested field has a value greater than ``8.5`` @@ -122,7 +118,7 @@ documents that meet the following criteria: $movies = Movie::where('year', 2010) ->where('imdb.rating', '>', 8.5) ->get(); - + return view('browse_movies', [ 'movies' => $movies ]); @@ -149,132 +145,8 @@ documents that meet the following criteria: Plot: A documentary on Brazilian Formula One racing driver Ayrton Senna, who won the F1 world championship three times before his death at age 34. -.. _laravel-retrieve-query-builder: - -Use Laravel Queries to Retrieve Documents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can use Laravel's database query builder to run find operations instead of using Eloquent -models. To run the database query, import the ``DB`` facade into your controller file and use -Laravel's query builder syntax. - -This example uses Laravel's query builder to retrieve documents in which the value -of the ``imdb.votes`` nested field is ``350``. - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax - - Use the following syntax to specify the query: - - .. code-block:: php - - $movies = DB::connection('mongodb') - ->collection('movies') - ->where('imdb.votes', 350) - ->get(); - - .. tab:: Controller Method - :tabid: controller - - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: - - .. io-code-block:: - :copyable: true - - .. input:: - :language: php - - class MovieController - { - public function show() - { - $movies = DB::connection('mongodb') - ->collection('movies') - ->where('imdb.votes', 350) - ->get(); - - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } - - .. output:: - :language: none - :visible: false - - Title: Murder in New Hampshire: The Pamela Wojas Smart Story - Year: 1991 - Runtime: 100 - IMDB Rating: 5.9 - IMDB Votes: 350 - Plot: Pamela Smart knows exactly what she wants and is willing to do - anything to get it. She is fed up with teaching, and her marriage offers - little excitement. Looking for a way out she applies ... - - Title: Ah Fu - Year: 2000 - Runtime: 105 - IMDB Rating: 6.6 - IMDB Votes: 350 - Plot: After a 13-year imprisonment in Hong Kong, a kickboxer challenges the - current champion in order to restore his honor. - - Title: Bandage - Year: 2010 - Runtime: 119 - IMDB Rating: 7 - IMDB Votes: 350 - Plot: Four boys have their friendship and musical talents tested in the ever - changing worlds of the music industry and real life in 1990s Japan. - - Title: Great Migrations - Year: 2010 - Runtime: 45 - IMDB Rating: 8.2 - IMDB Votes: 350 - Plot: Great Migrations takes viewers on the epic journeys animals undertake to - ensure the survival of their species. - - Then, make the following changes to your Laravel Quick Start application: - - - Import the ``DB`` facade into your ``MovieController.php`` file by adding the - ``use Illuminate\Support\Facades\DB`` use statement - - Replace the contents of your ``browse_movies.blade.php`` file with the following code: - - .. code-block:: php - - - - - Browse Movies - - -

Movies

- - @forelse ($movies as $movie) -

- Title: {{ $movie['title'] }}
- Year: {{ $movie['year'] }}
- Runtime: {{ $movie['runtime'] }}
- IMDB Rating: {{ $movie['imdb']['rating'] }}
- IMDB Votes: {{ $movie['imdb']['votes'] }}
- Plot: {{ $movie['plot'] }}
-

- @empty -

No results

- @endforelse - - - - - .. note:: - - Since the Laravel query builder returns data as an array rather than as instances of the Eloquent model class, - the view accesses the fields by using the array syntax instead of the ``->`` object operator. +To learn how to query by using the Laravel query builder instead of the +Eloquent ORM, see the :ref:`laravel-query-builder` page. .. _laravel-retrieve-all: @@ -295,10 +167,10 @@ Use the following syntax to run a find operation that matches all documents: .. warning:: The ``movies`` collection in the Atlas sample dataset contains a large amount of data. - Retrieving and displaying all documents in this collection might cause your web - application to time out. - - To avoid this issue, specify a document limit by using the ``take()`` method. For + Retrieving and displaying all documents in this collection might cause your web + application to time out. + + To avoid this issue, specify a document limit by using the ``take()`` method. For more information about ``take()``, see the :ref:`laravel-modify-find` section of this guide. @@ -462,7 +334,7 @@ field. IMDB Votes: 620 Plot: A documentary of black art. -.. tip:: +.. tip:: To learn more about sorting, see the following resources: From 10f44bf280b32c35931340b54da1aa6dbf2671d1 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Mon, 8 Apr 2024 16:42:14 -0400 Subject: [PATCH 14/14] DOCSP-35976: Delete One usage example (#2821) Adds a usage example page demonstrating how to delete one document from a collection --------- Co-authored-by: norareidy --- .../includes/usage-examples/DeleteOneTest.php | 40 +++++++++++ docs/usage-examples.txt | 1 + docs/usage-examples/deleteOne.txt | 69 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 docs/includes/usage-examples/DeleteOneTest.php create mode 100644 docs/usage-examples/deleteOne.txt diff --git a/docs/includes/usage-examples/DeleteOneTest.php b/docs/includes/usage-examples/DeleteOneTest.php new file mode 100644 index 000000000..1a2acd4e0 --- /dev/null +++ b/docs/includes/usage-examples/DeleteOneTest.php @@ -0,0 +1,40 @@ + 'Quiz Show', + 'runtime' => 133, + ], + ]); + + // begin-delete-one + $deleted = Movie::where('title', 'Quiz Show') + ->orderBy('_id') + ->limit(1) + ->delete(); + + echo 'Deleted documents: ' . $deleted; + // end-delete-one + + $this->assertEquals(1, $deleted); + $this->expectOutputString('Deleted documents: 1'); + } +} diff --git a/docs/usage-examples.txt b/docs/usage-examples.txt index 2bcd9ac58..32e876fa7 100644 --- a/docs/usage-examples.txt +++ b/docs/usage-examples.txt @@ -73,3 +73,4 @@ calls the controller function and returns the result to a web interface. /usage-examples/findOne /usage-examples/updateOne + /usage-examples/deleteOne diff --git a/docs/usage-examples/deleteOne.txt b/docs/usage-examples/deleteOne.txt new file mode 100644 index 000000000..762cfd405 --- /dev/null +++ b/docs/usage-examples/deleteOne.txt @@ -0,0 +1,69 @@ +.. _laravel-delete-one-usage: + +================= +Delete a Document +================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: delete one, remove, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +You can delete a document in a collection by retrieving a single Eloquent model and calling +the ``delete()`` method, or by calling ``delete()`` directly on a query builder. + +To delete a document, pass a query filter to the ``where()`` method, sort the matching documents, +and call the ``limit()`` method to retrieve only the first document. Then, delete this matching +document by calling the ``delete()`` method. + +Example +------- + +This usage example performs the following actions: + +- Uses the ``Movie`` Eloquent model to represent the ``movies`` collection in the + ``sample_mflix`` database +- Deletes a document from the ``movies`` collection that matches a query filter + +The example calls the following methods on the ``Movie`` model: + +- ``where()``: matches documents in which the value of the ``title`` field is ``'Quiz Show'`` +- ``orderBy()``: sorts matched documents by their ascending ``_id`` values +- ``limit()``: retrieves only the first matching document +- ``delete()``: deletes the retrieved document + +.. io-code-block:: + :copyable: true + + .. input:: ../includes/usage-examples/DeleteOneTest.php + :start-after: begin-delete-one + :end-before: end-delete-one + :language: php + :dedent: + + .. output:: + :language: console + :visible: false + + Deleted documents: 1 + +For instructions on editing your Laravel application to run the usage example, see the +:ref:`Usage Example landing page `. + +.. tip:: + + To learn more about deleting documents with {+odm-short+}, see the `Deleting Models + `__ section of the + Laravel documentation. + + For more information about query filters, see the :ref:`laravel-retrieve-matching` section of + the Read Operations guide. +