Model association
Eloquent: Association
- Introduction
- Definition of Association
- Polymorphic association
- Query association
- Preloading
- ##Inserting & updating associated models
- save
Method
- create
Method
- UpdateBelongs To
Association
- Many-to-many association
##Update parent set timestamp - save
Database tables are usually related to each other. For example, a blog post may have many comments, or an order may correspond to a user who placed the order. Eloquent makes it easy to manage and use these associations, and supports multiple types of associations:
Define Association
Eloquent Association is presented as a method in the Eloquent model class. Like the Eloquent model itself, associations can also be used as a powerful query statement builder, providing powerful chain calls and query functions. For example, we can attach a constraint to the chained call of the posts
association:
$user->posts()->where('active', 1)->get();
But before we dive into using associations, let’s learn how to define each association type.
One-to-one
One-to-one is the most basic relationship. For example, a User
model might be associated with a Phone
model. In order to define this association, we need to write a phone
method in the User
model. Call the hasOne
method inside the phone
method and return its result:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Get the phone record associated with the user. */ public function phone() { return $this->hasOne('App\Phone'); } }
hasOne
The first parameter of the method is the class name of the associated model . Once the model association is defined, we can use Eloquent dynamic properties to obtain the related records. Dynamic properties allow you to access relational methods just like properties defined in the model:
$phone = User::find(1)->phone;
Eloquent will determine the foreign key name based on the model name. In this case, it is automatically assumed that the Phone
model has a user_id
foreign key. If you want to override this convention, you can pass a second argument to the hasOne
method:
return $this->hasOne('App\Phone', 'foreign_key');
Additionally, Eloquent assumes that the value of the foreign key is the same as the parent id (or a custom $primaryKey) column values match. In other words, Eloquent will look for a value in the user_id column of the Phone record that matches the id column of the Users table. If you want the association to use a custom key name other than id, you can pass the third parameter to the hasOne method:
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
Define the reverse association
We have The Phone
model can be accessed from the User
model. Now, let's define another association on the Phone
model that will give us access to the User
model that owns the phone. We can define a reverse association using the belongsTo
method corresponding to the hasOne
method:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Phone extends Model{ /** * 获得拥有此电话的用户 */ public function user() { return $this->belongsTo('App\User'); } }
In the above example, Eloquent will try to match Phone
user_id
on the model to id
on the User
model. It determines the default foreign key name by examining the name of the relationship method and using _id
as the suffix. However, if the Phone
model's foreign key is not user_id
, then a custom key name can be passed as the second parameter to the belongsTo
method:
/** * 获得拥有此电话的用户 */ public function user(){ return $this->belongsTo('App\User', 'foreign_key'); }
If the parent model does not use id
as the primary key, or if you want to use different fields to connect the child model, you can pass the third parameter to the belongsTo
method. Specify the custom key of the parent data table in the form:
/** * 获得拥有此电话的用户 */ public function user(){ return $this->belongsTo('App\User', 'foreign_key', 'other_key'); }
One-to-many
The "one-to-many" association is used to define a single model to have any number of other associated models. For example, a blog post may have an infinite number of comments. Like all other Eloquent relationships, the definition of a one-to-many relationship is to write a method in the Eloquent model:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model{ /** * 获取博客文章的评论 */ public function comments() { return $this->hasMany('App\Comment'); } }
Remember, Eloquent will automatically determine the foreign key of the Comment
model Attributes. By convention, Eloquent will use the "snake case" form of the model name, plus the _id
suffix as the foreign key field. Therefore, in the above example, Eloquent will assume that the foreign key on the Comment
corresponding to the Post
model is post_id
.
Once the relationship is defined, the collection of comments can be obtained by accessing the comments
property of the Post
model. Remember, since Eloquent provides "dynamic properties", we can access the relationship method just like accessing the model's properties:
$comments = App\Post::find(1)->comments;foreach ($comments as $comment) { //}
Of course, since all relationships can also be used as query statement builders, you can Use chained calls to add additional constraints on the comments
method:
$comment = App\Post::find(1)->comments()->where('title', 'foo')->first();
Just like the hasOne
method, you can also use hasMany
method, pass additional parameters to override the default foreign keys and local keys used:
return $this->hasMany('App\Comment', 'foreign_key'); return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
One-to-many (Reverse)
Now, we have been able to obtain all the comments of an article, and then define an association to obtain the article through the comments. This association is the reverse association of the hasMany
association. It needs to be defined in the child model using the belongsTo
method:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model{ /** * 获取此评论所属文章 */ public function post() { return $this->belongsTo('App\Post'); } }
After this relationship is defined, we can Get the associated Post
model by accessing the post
"dynamic attribute" of the Comment
model:
$comment = App\Comment::find(1); echo $comment->post->title;
In the above example, Eloquent will try to match the post_id
of the Comment
model with the id
of the Post
model. The default foreign key name is determined by Eloquent based on the association name, followed by _ and the primary key field name as a suffix. Of course, if the foreign key of the Comment
model is not post_id
, then the custom key name can be passed as the second parameter to the belongsTo
method:
/** * 获取此评论所属文章 */ public function post(){ return $this->belongsTo('App\Post', 'foreign_key'); }
If the parent model does not use id
as the primary key, or if you want to use different fields to connect the child model, you can pass the third parameter to the belongsTo
method. Specify the custom key of the parent data table in the form:
/** * 获取此评论所属文章 */ public function post(){ return $this->belongsTo('App\Post', 'foreign_key', 'other_key'); }
Many-to-Many
Many-to-many relationships are slightly more complex than hasOne
and hasMany
relationships. For example, a user can have many roles, and these roles are shared by other users. For example, many users may have the "Administrator" role. To define this association, three database tables are required: users
, roles
, and role_user
. The role_user
table is named in alphabetical order from the two associated models, and contains the user_id
and role_id
fields.
Many-to-many relationships are defined by calling the result returned by this internal method belongsToMany
. For example, we define the roles
method in the User
model. :
<?php namespace App; use Illuminate\Database\Eloquent\Model;class User extends Model{ /** * 用户拥有的角色 */ public function roles() { return $this->belongsToMany('App\Role'); } }
Once the association is defined, you can obtain the user role through the roles
dynamic attribute:
$user = App\User::find(1); foreach ($user->roles as $role) { // }
Of course, like all other association models, you can use roles
method, using chain calls to add constraints to the query statement:
$roles = App\User::find(1)->roles()->orderBy('name')->get();
As mentioned earlier, in order to determine the table name of the associated connection table, Eloquent will connect the two in alphabetical order The name of an associated model. Of course, you can also not use this convention and pass the second parameter to the belongsToMany
method:
return $this->belongsToMany('App\Role', 'role_user');
In addition to customizing the table name of the connection table, you can also pass additional Parameters to the belongsToMany
method to define the key names of the fields in the table. The third parameter is the foreign key name of the model that defines this association in the connection table, and the fourth parameter is the foreign key name of another model in the connection table:
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
Definition Reverse association
To define a many-to-many reverse association, you only need to call the belongsToMany
method in the association model. We define the users
method in the Role
model:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Role extends Model{ /** * 拥有此角色的用户。 */ public function users() { return $this->belongsToMany('App\User'); } }
As you can see, except for the introduction of the model App\User
, other Exactly the same as defined in the User
model. Since we reuse the belongsToMany
method, the custom join table table name and the field name of the key in the custom join table are also applicable here.
Get the intermediate table fields
As you just learned, the many-to-many relationship requires an intermediate table to provide support. Eloquent provides some useful methods to interact with this table. . For example, suppose our User
object is associated with multiple Role
objects. After obtaining these related objects, you can use the pivot
attribute of the model to access the data of the intermediate table:
$user = App\User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; }
It should be noted that each Role
model we obtain Object will be automatically assigned the pivot
attribute, which represents a model object of the intermediate table and can be used like other Eloquent models.
By default, the pivot
object only contains the primary keys of the two related models. If there are other additional fields in your intermediate table, you must explicitly indicate it when defining the relationship:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
If you want the intermediate table to automatically maintain created_at
and updated_at
timestamps, then append the withTimestamps
method when defining the association:
return $this->belongsToMany('App\Role')->withTimestamps();
Custompivot
Attribute name
As mentioned earlier, attributes from the intermediate table can be accessed using the pivot
attribute . However, you are free to customize the name of this property to better reflect its use in your application.
For example, if your app contains users who may subscribe, there may be a many-to-many relationship between users and blogs. If this is the case, you may want to name the intermediate table accessor subscription
instead of pivot
. This can be done using the as
method when defining the relationship:
return $this->belongsToMany('App\Podcast') ->as('subscription') ->withTimestamps();
Once the definition is complete, you can access the intermediate table data using a custom name:
$users = User::with('podcasts')->get(); foreach ($users->flatMap->podcasts as $podcast) { echo $podcast->subscription->created_at; }
Filter relationships through intermediate tables
When defining relationships, you can also use the wherePivot
and wherePivotIn
methods to filter the results returned by belongsToMany
:
return $this->belongsToMany('App\Role')->wherePivot('approved', 1); return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);
Define the intermediate table model
Define the custom intermediate table model
If you want to define a custom model to represent the intermediate table in the association relationship, you can call using# when defining the association. ## method. Customized
Many-to-manyIntermediate table models must extend from the Illuminate\Database\Eloquent\Relations\Pivot class, custom
Many-to-many (polymorphic)Intermediate table models must inherit Illuminate \Database\Eloquent\Relations\MorphPivot class. For example,
When we write the association of the Role model, we use a custom intermediate table model
UserRole
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Role extends Model{ /** * 拥有此角色的所有用户 */ public function users() { return $this->belongsToMany('App\User')->using('App\UserRole'); } }When defining the
UserRole model, we need to extend
Pivot Class:
<?php namespace App; use Illuminate\Database\Eloquent\Relations\Pivot; class UserRole extends Pivot{ // }You can use a combination of
using and
withPivot to retrieve columns from an intermediate table. For example, by passing the column name to the
withPivot method, you can retrieve the
created_by and
updated_by columns from the
UserRole intermediate table .
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Role extends Model{ /** * 拥有此角色的用户。 */ public function users() { return $this->belongsToMany('App\User') ->using('App\UserRole') ->withPivot([ 'created_by', 'updated_by' ]); } }Custom relay model with increasing IDIf you define a many-to-many relationship with a custom relay model, and this The relay model has an auto-incrementing primary key. You should ensure that the custom relay model class defines an
incrementing attribute with a value of
true:
/** * 标识 ID 是否自增。 * * @var bool */ public $incrementing = true;Remote one-to-one relationshipRemote one-to-one relationship is implemented through an intermediate relationship model.
For example, if each provider has a user, and each user is associated with a user history, then the provider can access the user's history through the user, let's look at the database required to define this relationship Table:
suppliers id - integer users id - integer supplier_id - integer history id - integer user_id - integerAlthough the
history table does not contain the
supplier_id, the
hasOneThrough relationship can provide access to the user's history to access the supplier Model. Now that we have inspected the table structure of the relationship, let us define the corresponding method on the
Supplier model:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Supplier extends Model{ /** * 用户的历史记录。 */ public function userHistory() { return $this->hasOneThrough('App\History', 'App\User'); } }The first parameter passed to the
hasOneThrough method is The name of the model you wish to access. The second parameter is the name of the intermediate model.
hasOneThrough method. The third parameter represents the foreign key name of the intermediate model, and the fourth parameter represents the final The foreign key name of the model. The fifth parameter represents the local key name, and the sixth parameter represents the local key name of the intermediate model:
class Supplier extends Model{ /** * 用户的历史记录。 */ public function userHistory() { return $this->hasOneThrough( 'App\History', 'App\User', 'supplier_id', // 用户表外键 'user_id', // 历史记录表外键 'id', // 供应商本地键 'id' // 用户本地键 ); } }
Remote one-to-many association
Remote one-to-many association provides a convenient and short way to obtain remote-level associations through intermediate associations. For example, a Country
model can have multiple Post
models via an intervening User
model. In this example, you can easily collect all blog posts from a given country. Let's take a look at the data tables required to define this association:
countries id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string
Although the posts
table does not contain the country_id
field, hasManyThrough
Association allows us to access all user posts in a country through $country->posts
. To complete this query, Eloquent will first check the country_id
field of the intermediate table users
, and after finding all matching user IDs, use these IDs in the posts
table Complete the search.
Now that we know the data table structure required to define this association, let's define it in the Country
model:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Country extends Model{ /** * 当前国家所有文章。 */ public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); } }
## The first parameter of the #hasManyThrough method is the name of the model we ultimately wish to access, while the second parameter is the name of the intermediate model.
hasManyThrough method. The third parameter represents the foreign key name of the intermediate model, and the fourth parameter represents the final The foreign key name of the model. The fifth parameter represents the local key name, and the sixth parameter represents the local key name of the intermediate model:
class Country extends Model{ public function posts() { return $this->hasManyThrough( 'App\Post', 'App\User', 'country_id', // 用户表外键 'user_id', // 文章表外键 'id', // 国家表本地键 'id' // 用户表本地键 ); } }Polymorphic association Polymorphic associations allow a target model to be subordinate to multiple models with a single association. One-to-one (polymorphic)Table structureOne-to-one polymorphic association is similar to a simple one-to-one association; however, the target model can belong to multiple models on a single association. For example, the blogs
Post and
User might share a relationship to the
Image model. Using a one-to-one polymorphic association allows using a unique list of images for both blog posts and user accounts. Let's look at the table structure first:
posts id - integer name - string users id - integer name - string images id - integer url - string imageable_id - integer imageable_type - stringPay special attention to the
images table's
imageable_id and
imageable_type columns. The
imageable_id column contains the ID value of the article or user, while the
imageable_type column contains the class name of the parent model. Eloquent uses the
imageable_type column when accessing
imageable to determine the "type" of the parent model.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Image extends Model{ /** * 获取拥有此图片的模型。 */ public function imageable() { return $this->morphTo(); } } class Post extends Model{ /** * 获取文章图片。 */ public function image() { return $this->morphOne('App\Image', 'imageable'); } } class User extends Model{ /** * 获取用户图片。 */ public function image() { return $this->morphOne('App\Image', 'imageable'); } }
Get the association
Once the table and model are defined, this association can be accessed through the model. For example, to get the article image, you can use the image
dynamic attribute:
$post = App\Post::find(1); $image = $post->image;
You can also get the parent from the polymorphic model by accessing the method name called by morphTo
Model. In this case, it’s the Image
model’s imageable
method. So, we can access this method like a dynamic property:
$image = App\Image::find(1); $imageable = $image->imageable;
Image
The model's imageable
association will return either Post
or User
Instance, the result depends on which model the image attribute is.
One-to-many (polymorphic)
Table structure
One-to-many polymorphic associations are similar to simple one-to-many associations; however, the target model can belong to multiple models in a single association. Assume that users in the application can "comment" on articles and videos at the same time. Using polymorphic associations, these situations can be satisfied simultaneously with a single comments
table. Let’s first take a look at the table structure used to build this association:
posts id - integer title - string body - text videos id - integer title - string url - string comments id - integer body - text commentable_id - integer commentable_type - string
Model structure
Next, let’s look at the model definition for building this association:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model{ /** * 获取拥有此评论的模型。 */ public function commentable() { return $this->morphTo(); } } class Post extends Model{ /** * 获取此文章的所有评论。 */ public function comments() { return $this->morphMany('App\Comment', 'commentable'); } } class Video extends Model{ /** * 获取此视频的所有评论。 */ public function comments() { return $this->morphMany('App\Comment', 'commentable'); } }
Getting the association
Once the database table and model are defined, the association can be accessed through the model. For example, you can access all comments for an article using the comments
dynamic property:
$post = App\Post::find(1);foreach ($post->comments as $comment) { // }
You can also get its ownership from the polymorphic model by accessing the name of the method that performs the morphTo
call Model. In this case, the Comment
model's commentable
method:
$comment = App\Comment::find(1); $commentable = $comment->commentable;
Comment
The model's commentable
association will return Post
or Video
instance, the result depends on the model the comment belongs to.
morphOne and
morphMany associations. For example, the Blog
Post and
Video models can share a polymorphic relationship to the
Tag model. Using a many-to-many polymorphic association allows sharing between blog posts and videos using a unique tag. The following is the table structure of a many-to-many polymorphic association:
posts id - integer name - string videos id - integer name - string tags id - integer name - string taggables tag_id - integer taggable_id - integer taggable_type - stringModel structureNext, define the association on the model.
Post and
Video models both have
tags methods that call the
morphToMany method on the Eloquent base class:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model{ /** * 获取文章的所有标签。 */ public function tags() { return $this->morphToMany('App\Tag', 'taggable'); } }
Define the reverse association relationship
Next, you need to define a method for each association model on the Tag
model. In this example, we will define the posts
method and the videos
method:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Tag extends Model{ /** * 获取被打上此标签的所有文章。 */ public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } /** * 获取被打上此标签的所有视频。 */ public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } }
Get the association
Once defined Once you have a database table and model, you can access the relationship through the model. For example, you can use the tags
dynamic property to access all tags of an article:
$post = App\Post::find(1);foreach ($post->tags as $tag) { // }
You can also access the method name that performs the morphedByMany
method call to get its ownership from the polymorphic model Model. In this example, it's the Tag
model's posts
or videos
method. These methods can be accessed like dynamic properties:
$tag = App\Tag::find(1);foreach ($tag->videos as $video) { // }
Custom Polymorphic Type
By default, Laravel Store associated model types using fully qualified class names. In the one-to-many example above, since Comment
may belong to a Post
or a Video
, the default commentable_type
will be It's App\Post
or App\Video
. However, you may want to decouple the database from the internal structure of your application. In this case, you can define a "morph mapping" to tell Eloquent to use a custom name instead of the corresponding class name:
use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ 'posts' => 'App\Post', 'videos' => 'App\Video', ]);
Can be done in AppServiceProvider
's boot
Register morphMap
in the function, or create a separate service provider.
Query Association
Since all types of Eloquent associations are defined by methods, you can call these methods instead There is no need to actually perform the associated query. In addition, all Eloquent relation types act as query builders, allowing you to continuously add constraints through a chain of calls before executing SQL on the database.
For example, suppose a blog system's User
model has many related Post
models:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 获取该用户的所有文章。 */ public function posts() { return $this->hasMany('App\Post'); } }
You can query posts
Association and add additional constraints to it:
$user = App\User::find(1); $user->posts()->where('active', 1)->get();
You can use any query builder method on the association, please consult the query builder documentation to learn those methods that may be useful to you.
Relation Methods Vs. Dynamic Properties
If you do not need to add additional constraints to the Eloquent correlation query, you can access the relationship like a property. For example, continuing to use the User
and Post
example models, you can access all of the user's posts like this:
$user = App\User::find(1); foreach ($user->posts as $post) { // }
Dynamic attributes are "lazy loaded", which means that they It is only loaded when you actually access the associated data. Therefore, developers often use preloading to preload relationships that they know they will access after loading the model. For SQL queries that must be executed when loading model associations, eager loading significantly reduces the number of query executions.
Query existing relationships
When accessing model records, you may want to limit the query results based on the existence of the relationship . For example, if you want to get all articles with at least one comment, you can pass the association name to the has
and orHas
methods:
// 获取至少存在一条评论的所有文章... $posts = App\Post::has('comments')->get();
You can also specify the operators and Quantity further customizes the query:
// 获取评论超过三条的文章... $posts = App\Post::has('comments', '>=', 3)->get();
You can also use "dot" syntax to construct nested has
statements. For example, you can get articles with at least one comment and vote:
// 获取拥有至少一条带有投票评论的文章... $posts = App\Post::has('comments.votes')->get();
If you need more functions, you can use the whereHas
and orWhereHas
methods to put the "where" condition Go to has
query. These methods allow you to add custom constraints to the association, such as checking the comment content:
use Illuminate\Database\Eloquent\Builder; // 获取至少带有一条评论内容包含 foo% 关键词的文章... $posts = App\Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%');})->get(); // 获取至少带有十条评论内容包含 foo% 关键词的文章... $posts = App\Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%'); }, '>=', 10)->get();
Query for non-existent associations
When accessing model records, you may wish to limit query results based on the non-existence of the association. Suppose you want to get articles that doesntHave and orDoesntHave
have no comments on, you can do this by passing the association name to the doesntHave
and
methods:
if For more functionality, you can add "where" conditions to the $posts = App\Post::doesntHave('comments')->get();
doesntHave
query using the whereDoesntHave
and
methods. These methods allow you to add custom restrictions to the association, such as detecting comment content:
use Illuminate\Database\Eloquent\Builder; $posts = App\Post::whereDoesntHave('comments', function (Builder $query) { $query->where('content', 'like', 'foo%'); })->get();
You can also use "dot" syntax to perform nested association queries. For example, the following query retrieves articles with comments from authors who have not been banned: use Illuminate\Database\Eloquent\Builder;
$posts = App\Post::whereDoesntHave('comments.author', function (Builder $query)
{
$query->where('banned', 1);
})->get();
Associated model count
If you want to only calculate the statistical number of associated results without actually loading them, you can use the withCount
method, which will be placed in the of the result model {relation}_count
column. An example is as follows:
$posts = App\Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; }
You can add "counts" to multiple relationships just like adding restrictions to queries:
$posts = App\Post::withCount(['votes', 'comments' => function ($query) { $query->where('content', 'like', 'foo%'); }])->get(); echo $posts[0]->votes_count;echo $posts[0]->comments_count;
You can also alias the relationship count results, which allows you to add "counts" on the same relationship Multiple counts:
$posts = App\Post::withCount([ 'comments', 'comments as pending_comments_count' => function ($query) { $query->where('approved', false); }])->get(); echo $posts[0]->comments_count; echo $posts[0]->pending_comments_count;
If you assemble withCount
and select
queries together, make sure to call withCount after the
select method
:
$query = App\Post::select(['title', 'body'])->withCount('comments'); echo $posts[0]->title; echo $posts[0]->body; echo $posts[0]->comments_count;
Preloading
When accessing Eloquent associations in attribute mode, the associated data is "lazy" load". This way the associated data won't actually be loaded until the first time the property is accessed. However, Eloquent can "preload" child relationships when querying the parent model. Eager loading can alleviate the N 1 query problem. To illustrate the N 1 query problem, consider the situation where the Book
model is associated with Author
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Book extends Model{ /** * 获取书籍作者。 */ public function author() { return $this->belongsTo('App\Author'); } }
Now, let's get all the books and their authors:
$books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; }
This loop will execute a query to get all the books, and then for each book, execute a query to get the author. If we have 25 books, this loop will run 26 queries: 1 to query the book, and 25 additional queries to query the author of each book.
Thankfully we were able to use eager loading to condense the operation down to just 2 queries. At query time, you can use the with
method to specify the association you want to preload:
$books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; }
In this example, only two queries were executed:
select * from books select * from authors where id in (1, 2, 3, 4, 5, ...)
Preload multiple associations
Sometimes, you may need to preload several different associations in a single operation. To achieve this, just pass an array parameter consisting of multiple associated names to the with
method:
$books = App\Book::with(['author', 'publisher'])->get();
Nested preloading
can be used "Dot" syntax preloads nested associations. For example, preloading all book authors and their contact information in an Eloquent statement:
$books = App\Book::with('author.contacts')->get();
Preloading specified columns
It is not always necessary to obtain every column of the relationship. In this case, Eloquent allows you to specify the columns you want to get for the association:
$users = App\Book::with('author:id,name')->get();
{note} When using this feature, be sure to include ## in the list of columns to get. #id
column.
Add constraints for preloading
Sometimes, you may want to preload a relationship and add additional query conditions to the preload query, like the following example:
$users = App\User::with(['posts' => function ($query) { $query->where('title', 'like', '%first%'); }])->get();
In this example , Eloquent will preload only those articles whose title
column contains the first
keyword. You can also call other query builder methods to further customize the preloading operation:
$users = App\User::with(['posts' => function ($query) { $query->orderBy('created_at', 'desc'); }])->get();
PreloadingMaybe you also want to perform eager loading after the model is loaded. . For example, if you want to dynamically load related data, then the{note} When constraining preloading,
limit
andtake# cannot be used ## Query constructor method.
load method will be very useful for you:
$books = App\Book::all(); if ($someCondition) { $books->load('author', 'publisher'); }If you want to load the query in a thirsty manner For conditional constraints, you can load it in the form of an array. The key is the corresponding association and the value is the
Closure closure function. The parameter of the closure is a
query Example:
$books->load(['author' => function ($query) { $query->orderBy('published_date', 'asc'); }]);When the association is not loaded, you can use the
loadMissing method:
public function format(Book $book){ $book->loadMissing('author'); return [ 'name' => $book->name, 'author' => $book->author->name ]; }Nested lazy loading&
morphTo
If you want to quickly load the morphTo relationship, as well as the nested relationships on the various entities that the relationship may return, you can use the
loadMorph method.
morphTo relationship as its first parameter, and the second parameter receives the model array and relationship array. To help illustrate this approach, take a look at the following model example:
<?php use Illuminate\Database\Eloquent\Model; class ActivityFeed extends Model{ /** * Get the parent of the activity feed record. */ public function parentable() { return $this->morphTo(); } }In this example, let us assume
Event ,
Photo and
Post Models can create
ActivityFeed models. Furthermore, let us assume that the
Event model belongs to the
Calendar model, the
Photo model is associated with the
Tag model, and the
Post Model belongs to
Author model.
ActivityFeed model instance and load all
parentable models and their respective nested relationships at once:
$activities = ActivityFeed::with('parentable') ->get() ->loadMorph('parentable', [ Event::class => ['calendar'], Photo::class => ['tags'], Post::class => ['author'], ]);Insert & update associated model
Save method
Eloquent provides a convenient method for adding associations to new models. For example, maybe you need to add a new Comment
to a Post
model. You don't need to manually set the post_id
attribute in Comment
, you can directly insert Comment
directly using the save
method of the associated model:
$comment = new App\Comment(['message' => 'A new comment.']); $post = App\Post::find(1); $post->comments()->save($comment);
It should be noted that we do not use dynamic attributes to access the comments
association. Instead, we call the comments
method to get the associated instance. The save
method will automatically add the appropriate post_id
value to the Comment
model.
If you need to save multiple associated models, you can use the saveMany
method:
$post = App\Post::find(1); $post->comments()->saveMany([ new App\Comment(['message' => 'A new comment.']), new App\Comment(['message' => 'Another comment.']), ]);
Save the model and associated data recursively
If you want to save
your model and all its associated data, you can use the push
method:
$post = App\Post::find(1); $post->comments[0]->message = 'Message'; $post->comments[0]->author->name = 'Author Name';$post->push();
New methods
In addition to the save
and saveMany
methods, you also You can use the create
method. It accepts an array of properties, creates a model and inserts it into the database. Also, the difference between the save
method and the create
method is that the save
method accepts a complete Eloquent model instance, while the create
Then accept ordinary PHP arrays:
$post = App\Post::find(1); $comment = $post->comments()->create([ 'message' => 'A new comment.', ]);
{tip} Before using the
create
method, please be sure to check the batch assignment chapter of this document.
You can also use the createMany
method to create multiple associated models:
$post = App\Post::find(1);$post->comments()->createMany([ [ 'message' => 'A new comment.', ], [ 'message' => 'Another new comment.', ], ]);
You can also use findOrNew
, firstOrNew
, firstOrCreate
and updateOrCreate
methods to create and update relational models.
UpdatebelongsTo
Association
When updating the belongsTo
association, you can use the associate
method. This method will set the foreign key in the child model:
$account = App\Account::find(10); $user->account()->associate($account);$user->save();
When removing the belongsTo
association, you can use the dissociate
method. This method will set the associated foreign key to null
:
$user->account()->dissociate();$user->save();
Default Model
belongsTo
The relationship allows you to specify a default model. When the given relationship is null
, the default model will be returned. This mode is called Null Object Mode and can reduce unnecessary checks in your code. In the example below, if no author is found for the published post, the user
relationship will return an empty App\User
model:
/** * 获取帖子的作者。 */ public function user(){ return $this->belongsTo('App\User')->withDefault(); }
if needed in the default model To add properties, you can pass an array or callback method to withDefault
:
/** * 获取帖子的作者。 */ public function user(){ return $this->belongsTo('App\User')->withDefault([ 'name' => 'Guest Author', ]); } /** * 获取帖子的作者。 */ public function user(){ return $this->belongsTo('App\User')->withDefault(function ($user) { $user->name = 'Guest Author'; }); }##Many-to-many association Attach/DetachEloquent also provides some additional auxiliary methods to make the use of related models more convenient. For example, let's assume that a user can have multiple roles, and each role can be shared by multiple users. Attaching a role to a user is achieved by inserting a record into the intermediate table. This operation can be completed using the
attach method:
$user = App\User::find(1); $user->roles()->attach($roleId);When attaching a relationship to a model, you can also pass A set of additional data to be inserted into the intermediate table:
$user->roles()->attach($roleId, ['expires' => $expires]);Of course, sometimes it is also necessary to remove the user's role. You can use
detach to remove many-to-many related records. The
detach method will remove the records corresponding to the intermediate table; but these two models will remain in the database:
// 移除用户的一个角色... $user->roles()->detach($roleId); // 移除用户的所有角色... $user->roles()->detach();For convenience,
attach and
detach also allows passing an array of IDs:
$user = App\User::find(1); $user->roles()->detach([1, 2, 3]); $user->roles()->attach([ 1 => ['expires' => $expires], 2 => ['expires' => $expires] ]);Synchronous association You can also use the
sync method to build a many-to-many association.
sync Method receives an array of IDs to replace the records of the intermediate table. Among the intermediate table records, all records that are not in the ID array will be removed. So after the operation is completed, only the ID of the given array will be retained in the intermediate table:
$user->roles()->sync([1, 2, 3]);You can also pass additional additional data to the intermediate table by ID:
$user->roles()->sync([1 => ['expires' => true], 2, 3]);If you If you don’t want to remove the existing ID, you can use the
syncWithoutDetaching method:
$user->roles()->syncWithoutDetaching([1, 2, 3]);Switch associationMany-to-many association is also provided
The toggle method is used to "toggle" the attached state of the given ID array. If the given ID is already appended in the intermediate table, then it will be removed. Likewise, if the given ID has been removed, it will be appended:
$user->roles()->toggle([1, 2, 3]);
App\User::find(1)->roles()->save($role, ['expires' => $expires]);
Update intermediate table records
If you need to update an existing record in the intermediate table, you can use updateExistingPivot
. This method receives the foreign key of the intermediate table and the data array to be updated for update:
$user = App\User::find(1); $user->roles()->updateExistingPivot($roleId, $attributes);
Update parent timestamp
When a model belongs to belongsTo
or belongsToMany
another model, for example Comment
belongs to Post
, sometimes updating the sub-model results in an update Parent model timestamps are very useful. For example, when the Comment
model is updated, you want to automatically "trigger" an update of the Updated_at
timestamp of the parent Post
model. Eloquent makes it easy. Just add a touches
attribute to the child model that contains the associated name:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model{ /** * 要触发的所有关联关系 * * @var array */ protected $touches = ['post']; /** * 评论所属的文章 */ public function post() { return $this->belongsTo('App\Post'); } }
Now, when you update a Comment
, the corresponding parent Post
The model's updated_at
field will also be updated, making it easier to know when to invalidate the cache of a Post
model:
$comment = App\Comment::find(1); $comment->text = 'Edit to this comment!'; $comment->save();