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); + } +}