diff --git a/docs/eloquent-models.txt b/docs/eloquent-models.txt index 3ce32c124..e7edadcfe 100644 --- a/docs/eloquent-models.txt +++ b/docs/eloquent-models.txt @@ -6,517 +6,29 @@ Eloquent Models .. facet:: :name: genre - :values: tutorial + :values: reference .. meta:: - :keywords: php framework, odm, code example - -This package includes a MongoDB enabled Eloquent class that you can use to -define models for corresponding collections. - -Extending the base model -~~~~~~~~~~~~~~~~~~~~~~~~ - -To get started, create a new model class in your ``app\Models\`` directory. - -.. code-block:: php - - namespace App\Models; - - 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); - } - } + :keywords: php framework, odm + +Eloquent models are part of the Laravel Eloquent object-relational +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/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/eloquent-models/relationships.txt b/docs/eloquent-models/relationships.txt new file mode 100644 index 000000000..2ae716132 --- /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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/RelationshipsExamplesTest.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/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/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 @@ +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 new file mode 100644 index 000000000..3379c866b --- /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); + } +} 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 @@ + + docs src tests @@ -36,5 +37,16 @@ + + + + docs/**/*.php + + + docs/**/*.php + + + docs/**/*.php +