Model association


Eloquent: Association

Introduction

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:

    One-to-one
  • One-to-many
  • Many-to-many
  • Remote one-to-one
  • Remote one-to-many
  • One-to-one (polymorphic association)
  • One-to-many (polymorphic association)
  • Many-to-many (polymorphic association)

##

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. CustomizedMany-to-manyIntermediate table models must extend from the Illuminate\Database\Eloquent\Relations\Pivot class, customMany-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 extendPivot 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 ID

If 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 relationship

Remote 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 - integer

Although 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.

When executing related queries, the foreign key names agreed by Eloquent are usually used. If you want to customize the associated key, you can do so by passing the third and fourth parameters to the

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.

When executing related queries, the foreign key names agreed by Eloquent are usually used. If you want to customize the associated keys, you can do so by passing the third and fourth parameters to the

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 structure

One-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 - string

Pay 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.

Model structure

Next, let’s look at the associated model definition:

<?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.

##Many-to-many (polymorphic)

Table structure

Many-to-many polymorphic associations are slightly more complex than

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 - string

Model structure

Next, 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

orDoesntHave

methods:

$posts = App\Post::doesntHave('comments')->get();
if For more functionality, you can add "where" conditions to the doesntHave query using the whereDoesntHave and

orWhereDoesntHave

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();

{note} When constraining preloading, limit and take# cannot be used ## Query constructor method.

Preloading

Maybe you also want to perform eager loading after the model is loaded. . For example, if you want to dynamically load related data, then the

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.

This method accepts the name of the

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.

Using these model definitions and relationships, we can retrieve the

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/Detach

Eloquent 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 anddetach 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 association

Many-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]);

Save additional data on the intermediate table

When dealing with many-to-many relationships, the save method receives an additional data array as the second parameter:

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();
this article First published on LearnKu.com website.