# Eloquent ORM —— 關聯關系
## 1、簡介
數據表經常要與其它表做關聯,比如一篇博客文章可能有很多評論,或者一個訂單會被關聯到下單用戶,Eloquent變得簡單,并且支持多種不同類型的關聯關系:
* [一對一](http://laravelacademy.org/post/140.html#one-one)
* [一對多](http://laravelacademy.org/post/140.html#one-many)
* [多對多](http://laravelacademy.org/post/140.html#many-many)
* [遠層一對多](http://laravelacademy.org/post/140.html#has-many-through)
* [多態關聯](http://laravelacademy.org/post/140.html#polymorphic-relations)
* [多對多的多態關聯](http://laravelacademy.org/post/140.html#many-many-polymorphic-relations)
## 2、定義關聯關系
Eloquent關聯關系以Eloquent模型類方法的形式被定義。和Eloquent模型本身一樣,關聯關系也是強大的[查詢構建器](http://laravelacademy.org/post/126.html),定義關聯關系為函數能夠提供功能強大的方法鏈和查詢能力。例如:
~~~
$user->posts()->where('active', 1)->get();
~~~
但是,在深入使用關聯關系之前,讓我們先學習如何定義每種關聯類型:
### 2.1?一對一
一對一關聯是一個非常簡單的關聯關系,例如,一個`User`模型有一個與之對應的`Phone`模型。要定義這種模型,我們需要將`phone`方法置于`User`模型中,`phone`方法應該返回Eloquent模型基類上`hasOne`方法的結果:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model{
/**
* 獲取關聯到用戶的手機
*/
public function phone()
{
return $this->hasOne('App\Phone');
}
}
~~~
傳遞給`hasOne`方法的第一個參數是關聯模型的名稱,關聯關系被定義后,我們可以使用Eloquent的動態屬性獲取關聯記錄。動態屬性允許我們訪問關聯函數就像它們是定義在模型上的屬性一樣:
~~~
$phone = User::find(1)->phone;
~~~
Eloquent默認關聯關系的外鍵基于模型名稱,在本例中,`Phone`模型默認有一個`user_id`外鍵,如果你希望重寫這種約定,可以傳遞第二個參數到`hasOne`方法:
~~~
return $this->hasOne('App\Phone', 'foreign_key');
~~~
此外,Eloquent假設外鍵應該在父級上有一個與之匹配的`id`,換句話說,Eloquent將會通過`user`表的`id`值去`phone`表中查詢`user_id`與之匹配的`Phone`記錄。如果你想要關聯關系使用其他值而不是`id`,可以傳遞第三個參數到`hasOne`來指定自定義的主鍵:
~~~
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
~~~
### 2.1.1 定義相對的關聯
我們可以從`User`中訪問`Phone`模型,相應的,我們也可以在`Phone`模型中定義關聯關系從而讓我們可以擁有該`phone`的`User`。我們可以使用`belongsTo`方法定義與`hasOne`關聯關系相對的關聯:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model{
/**
* 獲取手機對應的用戶
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
~~~
在上面的例子中,Eloquent將會嘗試通過`Phone`模型的`user_id`去`User`模型查找與之匹配的記錄。Eloquent通過關聯關系方法名并在方法名后加`_id`后綴來生成默認的外鍵名。然而,如果`Phone`模型上的外鍵不是`user_id`,也可以將自定義的鍵名作為第二個參數傳遞到`belongsTo`方法:
~~~
/**
* 獲取手機對應的用戶
*/
public function user(){
return $this->belongsTo('App\User', 'foreign_key');
}
~~~
如果父模型不使用`id`作為主鍵,或者你希望使用別的列來連接子模型,可以將父表自定義鍵作為第三個參數傳遞給`belongsTo`方法:
~~~
/**
* 獲取手機對應的用戶
*/
public function user(){
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}
~~~
### 2.2?一對多
“一對多”是用于定義單個模型擁有多個其它模型的關聯關系。例如,一篇博客文章擁有無數評論,和其他關聯關系一樣,一對多關聯通過在Eloquent模型中定義方法來定義:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{
/**
* 獲取博客文章的評論
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
}
~~~
記住,Eloquent會自動判斷`Comment`模型的外鍵,為方便起見,Eloquent將擁有者模型名稱加上`id`后綴作為外鍵。因此,在本例中,Eloquent假設`Comment`模型上的外鍵是`post_id`。
關聯關系被定義后,我們就可以通過訪問`comments`屬性來訪問評論集合。記住,由于Eloquent提供“動態屬性”,我們可以像訪問模型的屬性一樣訪問關聯方法:
~~~
$comments = App\Post::find(1)->comments;
foreach ($comments as $comment) {
//
}
~~~
當然,由于所有關聯同時也是查詢構建器,我們可以添加更多的條件約束到通過調用`comments`方法獲取到的評論上:
~~~
$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();
~~~
和`hasOne`方法一樣,你還可以通過傳遞額外參數到`hasMany`方法來重新設置外鍵和本地主鍵:
~~~
return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
~~~
### 2.2.1 定義相對的關聯
現在我們可以訪問文章的所有評論了,接下來讓我們定義一個關聯關系允許通過評論訪問所屬文章。要定義與`hasMany`相對的關聯關系,需要在子模型中定義一個關聯方法去調用`belongsTo`方法:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model{
/**
* 獲取評論對應的博客文章
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
~~~
關聯關系定義好之后,我們可以通過訪問動態屬性`post`來獲取一條`Comment`對應的`Post`:
~~~
$comment = App\Comment::find(1);
echo $comment->post->title;
~~~
在上面這個例子中,Eloquent嘗試匹配`Comment`模型的`post_id`與`Post`模型的`id`,Eloquent通過關聯方法名加上`_id`后綴生成默認外鍵,當然,你也可以通過傳遞自定義外鍵名作為第二個參數傳遞到`belongsTo`方法,如果你的外鍵不是`post_id`,或者你想自定義的話:
~~~
/**
* 獲取評論對應的博客文章
*/
public function post(){
return $this->belongsTo('App\Post', 'foreign_key');
}
~~~
如果你的父模型不使用`id`作為主鍵,或者你希望通過其他列來連接子模型,可以將自定義鍵名作為第三個參數傳遞給`belongsTo`方法:
~~~
/**
* 獲取評論對應的博客文章
*/
public function post(){
return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
}
~~~
### 2.3?多對多
多對多關系比`hasOne`和`hasMany`關聯關系要稍微復雜一些。這種關聯關系的一個例子就是一個用戶有多個角色,同時一個角色被多個用戶共用。例如,很多用戶可能都有一個“Admin”角色。要定義這樣的關聯關系,需要三個數據表:`users`、`roles`和`role_user`,`role_user`表按照關聯模型名的字母順序命名,并且包含`user_id`和`role_id`兩個列。
多對多關聯通過編寫一個調用Eloquent基類上的`belongsToMany`方法的函數來定義:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model{
/**
* 用戶角色
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
~~~
關聯關系被定義之后,可以使用動態屬性`roles`來訪問用戶的角色:
~~~
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
~~~
當然,和所有其它關聯關系類型一樣,你可以調用`roles`方法來添加條件約束到關聯查詢上:
~~~
$roles = App\User::find(1)->roles()->orderBy('name')->get();
~~~
正如前面所提到的,為了決定關聯關系連接表的表名,Eloquent以字母順序連接兩個關聯模型的名字。然而,你可以重寫這種約定——通過傳遞第二個參數到`belongsToMany`方法:
~~~
return $this->belongsToMany('App\Role', 'user_roles');
~~~
除了自定義連接表的表名,你還可以通過傳遞額外參數到`belongsToMany`方法來自定義該表中字段的列名。第三個參數是你定義的關系模型的外鍵名稱,第四個參數你要連接到的模型的外鍵名稱:
~~~
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
~~~
### 2.3.1 定義相對的關聯關系
要定義與多對多關聯相對的關聯關系,只需在關聯模型中在調用一下`belongsToMany`方法即可。讓我們在`Role`模型中定義`users`方法:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model{
/**
* 角色用戶
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}
~~~
正如你所看到的,定義的關聯關系和與其對應的`User`中定義的一模一樣,只是前者引用`App\Role`,后者引用`App\User`,由于我們再次使用了`belongsToMany`方法,所有的常用表和鍵自定義選項在定義與多對多相對的關聯關系時都是可用的。
### 2.3.2 獲取中間表的列
正如你已經學習到的,處理多對多關聯要求一個中間表。Eloquent提供了一些有用的方法來與其進行交互,例如,我們假設`User`對象有很多與之關聯的`Role`對象,訪問這些關聯關系之后,我們可以使用模型上的`pivot`屬性訪問中間表:
~~~
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
~~~
注意我們獲取到的每一個`Role`模型都被自動賦上了`pivot`屬性。該屬性包含一個代表中間表的模型,并且可以像其它Eloquent模型一樣使用。
默認情況下,只有模型鍵才能用在`privot`對象上,如果你的`privot`表包含額外的屬性,必須在定義關聯關系時進行指定:
~~~
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
~~~
如果你想要你的`privot`表自動包含`created_at`和`updated_at`時間戳,在關聯關系定義時使用`withTimestamps`方法:
~~~
return $this->belongsToMany('App\Role')->withTimestamps();
~~~
### 2.4 遠層的一對多
“遠層一對多”關聯為通過中間關聯訪問遠層的關聯關系提供了一個便利之道。例如,`Country`模型通過中間的`User`模型可能擁有多個`Post`模型。在這個例子中,你可以輕易的聚合給定國家的所有文章,讓我們看看定義這個關聯關系需要哪些表:
~~~
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
~~~
盡管`posts`表不包含`country_id`列,`hasManyThrough`關聯提供了通過`$country->posts`來訪問一個國家的所有文章。要執行該查詢,Eloquent在中間表`$users`上檢查`country_id`,查找到相匹配的用戶ID后,通過用戶ID來查詢`posts`表。
既然我們已經查看了該關聯關系的數據表結構,接下來讓我們在`Country`模型上進行定義:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model{
/**
* 獲取指定國家的所有文章
*/
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
}
~~~
第一個傳遞到`hasManyThrough`方法的參數是最終我們希望訪問的模型的名稱,第二個參數是中間模型名稱。
當執行這種關聯查詢時通常Eloquent外鍵規則會被使用,如果你想要自定義該關聯關系的外鍵,可以將它們作為第三個、第四個參數傳遞給`hasManyThrough`方法。第三個參數是中間模型的外鍵名,第四個參數是最終模型的外鍵名。
~~~
class Country extends Model{
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
}
}
~~~
### 2.5 多態關聯
### 2.5.1?表結構
多態關聯允許一個模型在單個關聯下屬于多個不同模型。例如,假如你想要為產品和職工存儲照片,使用多態關聯,你可以在這兩種場景下使用單個`photos`表,首先,讓我們看看構建這種關聯關系需要的表結構:
~~~
staff
id - integer
name - string
products
id - integer
price - integer
photos
id - integer
path - string
imageable_id - integer
imageable_type - string
~~~
兩個重要的列需要注意的是`photos`表上的`imageable_id`和`imageable_type`。`imageable_id`列包含`staff`或`product`的ID值,而`imageable_type`列包含所屬模型的類名。當訪問`imageable`關聯時,ORM根據`imageable_type`列來判斷所屬模型的類型并返回相應模型實例。
### 2.5.2 ?模型結構
接下來,讓我們看看構建這種關聯關系需要在模型中定義什么:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Photo extends Model{
/**
* 獲取所有擁有的imageable模型
*/
public function imageable()
{
return $this->morphTo();
}
}
class Staff extends Model{
/**
* 獲取所有職員照片
*/
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}
class Product extends Model{
/**
* 獲取所有產品照片
*/
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}
~~~
### 2.5.3 獲取多態關聯
數據表和模型定義好以后,可以通過模型訪問關聯關系。例如,要訪問一個職員的所有照片,可以通過使用`photos`的動態屬性:
~~~
$staff = App\Staff::find(1);
foreach ($staff->photos as $photo) {
//
}
~~~
你還可以通過訪問調用`morphTo`方法名來從多態模型中獲取多態關聯的所屬對象。在本例中,就是`Photo`模型中的`imageable`方法。因此,我們可以用動態屬性的方式訪問該方法:
~~~
$photo = App\Photo::find(1);
$imageable = $photo->imageable;
~~~
`Photo`模型上的`imageable`關聯返回`Staff`或`Product`實例,這取決于那個類型的模型擁有該照片。
### 2.6 多對多多態關聯
### 2.6.1 表結構
除了傳統的多態關聯,還可以定義“多對多”的多態關聯,例如,一個博客的`Post`和`Video`模型可能共享一個`Tag`模型的多態關聯。使用對多對的多態關聯允許你在博客文章和視頻之間有唯一的標簽列表。首先,讓我們看看表結構:
~~~
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
~~~
### 2.6.2 模型結構
接下來,我們準備在模型中定義該關聯關系。`Post`和`Video`模型都有一個`tags`方法調用Eloquent基類的`morphToMany`方法:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{
/**
* 獲取指定文章所有標簽
*/
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
~~~
### 2.6.3 定義相對的關聯關系
接下來,在`Tag`模型中,應該為每一個關聯模型定義一個方法,例如,我們定義一個`posts`方法和`videos`方法:
~~~
<?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');
}
}
~~~
### 2.6.4 獲取關聯關系
定義好數據庫和模型后可以通過模型訪問關聯關系。例如,要訪問一篇文章的所有標簽,可以使用動態屬性`tags`:
~~~
$post = App\Post::find(1);
foreach ($post->tags as $tag) {
//
}
~~~
還可以通過訪問調用`morphedByMany`的方法名從多態模型中獲取多態關聯的所屬對象。在本例中,就是Tag模型中的`posts`或者`videos`方法:
~~~
$tag = App\Tag::find(1);
foreach ($tag->videos as $video) {
//
}
~~~
## 3、關聯查詢
由于Eloquent所有關聯關系都是通過函數定義,你可以調用這些方法來獲取關聯關系的實例而不需要再去手動執行關聯查詢。此外,所有Eloquent關聯關系類型同時也是查詢構建器,允許你在最終在數據庫執行SQL之前繼續添加條件約束到關聯查詢上。
例如,假定在一個博客系統中一個User模型有很多相關的Post模型:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model{
/**
* 獲取指定用戶的所有文章
*/
public function posts()
{
return $this->hasMany('App\Post');
}
}
~~~
你可以像這樣查詢`posts`關聯并添加額外的條件約束到該關聯關系上:
~~~
$user = App\User::find(1);
$user->posts()->where('active', 1)->get();
~~~
你可以在關聯關系上使用任何[查詢構建器](http://laravelacademy.org/post/126.html)!
**關聯關系方法 VS 動態屬性**
如果你不需要添加額外的條件約束到Eloquent關聯查詢,你可以簡單通過動態屬性來訪問關聯對象,例如,還是拿`User`和`Post`模型作為例子,你可以像這樣訪問所有用戶的文章:
~~~
$user = App\User::find(1);
foreach ($user->posts as $post) {
//
}
~~~
動態屬性就是”懶惰式加載來預加載他們知道在加載模型時要被訪問的關聯關系。渴求式加載有效減少了必須要被執以加載模型關聯的SQL查詢。
**查詢已存在的關聯關系**
訪問一個模型的記錄的時候,你可能希望基于關聯關系是否存在來限制查詢結果的數目。例如,假設你想要獲取所有至少有一個評論的博客文章,要實現這個,可以傳遞關聯關系的名稱到`has`方法:
~~~
// 獲取所有至少有一條評論的文章...
$posts = App\Post::has('comments')->get();
~~~
你還可以指定操作符和大小來自定義查詢:
~~~
// 獲取所有至少有三條評論的文章...
$posts = Post::has('comments', '>=', 3)->get();
~~~
還可以使用”.“來構造嵌套has語句,例如,你要獲取所有至少有一條評論及投票的所有文章:
~~~
// 獲取所有至少有一條評論獲得投票的文章...
$posts = Post::has('comments.votes')->get();
~~~
如果你需要更強大的功能,可以使用`whereHas`和`orWhereHas`方法將`where`條件放到`has`查詢上,這些方法允許你添加自定義條件約束到關聯關系條件約束,例如檢查一條評論的內容:
~~~
// 獲取所有至少有一條評論包含foo字樣的文章
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
~~~
### 3.1 渴求式加載
當以屬性方式訪問數據庫關聯關系的時候,關聯關系數據時”懶惰式加載“的,這意味著關聯關系數據直到第一次訪問的時候才被加載。然而,Eloquent可以在查詢父級模型的同時”渴求式加載“關聯關系。渴求式加載緩解了`N+1`查詢問題,要闡明`N+1`查詢問題,考慮下關聯到`Author`的`Book`模型:
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model{
/**
* 獲取寫這本書的作者
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
~~~
現在,讓我們獲取所有書及其作者:
~~~
$books = App\Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
~~~
該循環先執行1次查詢獲取表中的所有書,然后另一個查詢獲取每一本書的作者,因此,如果有25本書,要執行26次查詢:1次是獲取書本身,剩下的25次查詢是為每一本書獲取其作者。
謝天謝地,我們可以使用渴求式加載來減少該操作到2次查詢。當查詢的時候,可以使用`with`方法指定應該被渴求式加載的關聯關系:
~~~
$books = App\Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
~~~
在該操作中,只執行兩次查詢即可:
~~~
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)
~~~
### 3.1.1 渴求式加載多個關聯關系
有時候你需要在單個操作中渴求式加載幾個不同的關聯關系。要實現這個,只需要添加額外的參數到`with`方法即可:
~~~
$books = App\Book::with('author', 'publisher')->get();
~~~
### 3.1.2 嵌套的渴求式加載
要渴求式加載嵌套的關聯關系,可以使用”.“語法。例如,讓我們在一個Eloquent語句中渴求式加載所有書的作者及所有作者的個人聯系方式:
~~~
$books = App\Book::with('author.contacts')->get();
~~~
### 3.2 帶條件約束的渴求式加載
有時候我們希望渴求式加載一個關聯關系,但還想為渴求式加載指定更多的查詢條件:
~~~
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
~~~
在這個例子中,Eloquent只渴求式加載title包含first的文章。當然,你可以調用其它查詢構建器來自定義渴求式加載操作:
~~~
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->get();
~~~
### 3.3 懶惰渴求式加載
有時候你需要在父模型已經被獲取后渴求式加載一個關聯關系。例如,這在你需要動態決定是否加載關聯模型時可能很有用:
~~~
$books = App\Book::all();
if ($someCondition) {
$books->load('author', 'publisher');
}
~~~
如果你需要設置更多的查詢條件到渴求式加載查詢上,可以傳遞一個閉包到`load`方法:
~~~
$books->load(['author' => function ($query) {
$query->orderBy('published_date', 'asc');
}]);
~~~
## 4、插入關聯模型
### 4.1 基本使用
### 4.1.1 save方法
Eloquent提供了便利的方法來添加新模型到關聯關系。例如,也許你需要插入新的`Comment`到`Post`模型,你可以從關聯關系的`save`方法直接插入`Comment`而不是手動設置`Comment`的`post_id`屬性:
~~~
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$comment = $post->comments()->save($comment);
~~~
注意我們沒有用動態屬性方式訪問`comments`,而是調用`comments`方法獲取關聯關系實例。`save`方法會自動添加`post_id`值到新的`Comment`模型。
如果你需要保存多個關聯模型,可以使用`saveMany`方法:
~~~
$post = App\Post::find(1);
$post->comments()->saveMany([
new App\Comment(['message' => 'A new comment.']),
new App\Comment(['message' => 'Another comment.']),
]);
~~~
### 4.1.2 save & 多對多關聯
當處理多對多關聯的時候,`save`方法以數組形式接收額外的中間表屬性作為第二個參數:
~~~
App\User::find(1)->roles()->save($role, ['expires' => $expires]);
~~~
### 4.1.3 create方法
除了`save`和`saveMany`方法外,還可以使用`create`方法,該方法接收屬性數組、創建模型、然后插入數據庫。`save`和`create`的不同之處在于`save`接收整個Eloquent模型實例而`create`接收原生PHP數組:
~~~
$post = App\Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
~~~
使用`create`方法之前確保先瀏覽屬性[批量賦值](http://laravelacademy.org/post/138.html#ipt_kb_toc_138_13)文檔。
### 4.1.4 更新”屬于“關聯
更新`belongsTo`關聯的時候,可以使用`associate`方法,該方法會在子模型設置外鍵:
~~~
$account = App\Account::find(10);
$user->account()->associate($account);
$user->save();
~~~
移除`belongsTo`關聯的時候,可以使用`dissociate`方法。該方法在子模型上取消外鍵和關聯:
~~~
$user->account()->dissociate();
$user->save();
~~~
### 4.2 多對多關聯
### 4.2.1 附加/分離
處理多對多關聯的時候,Eloquent提供了一些額外的幫助函數來使得處理關聯模型變得更加方便。例如,讓我們假定一個用戶可能有多個角色同時一個角色屬于多個用戶,要通過在連接模型的中間表中插入記錄附加角色到用戶上,可以使用`attach`方法:
~~~
$user = App\User::find(1);
$user->roles()->attach($roleId);
~~~
附加關聯關系到模型,還可以以數組形式傳遞額外被插入數據到中間表:
~~~
$user->roles()->attach($roleId, ['expires' => $expires]);
~~~
當然,有時候有必要從用戶中移除角色,要移除一個多對多關聯記錄,使用`detach`方法。`detach`方法將會從中間表中移除相應的記錄;然而,兩個模型在數據庫中都保持不變:
~~~
// 從指定用戶中移除角色...
$user->roles()->detach($roleId);
// 從指定用戶移除所有角色...
$user->roles()->detach();
~~~
為了方便,`attach`和`detach`還接收數組形式的ID作為輸入:
~~~
$user = App\User::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
~~~
### 4.2.2 同步
你還可以使用`sync`方法構建多對多關聯。`sync`方法接收數組形式的ID并將其放置到中間表。任何不在該數組中的ID對應記錄將會從中間表中移除。因此,該操作完成后,只有在數組中的ID對應記錄還存在于中間表:
~~~
$user->roles()->sync([1, 2, 3]);
~~~
你還可以和ID一起傳遞額外的中間表值:
~~~
$user->roles()->sync([1 => ['expires' => true], 2, 3]);
~~~
### 4.3 觸發父級時間戳
當一個模型屬于另外一個時,例如`Comment`屬于`Post`,子模型更新時父模型的時間戳也被更新將很有用,例如,當`Comment`模型被更新時,你可能想要”觸發“創建其所屬模型`Post`的`updated_at`時間戳。Eloquent使得這項操作變得簡單,只需要添加包含關聯關系名稱的`touches`屬性到子模型中即可:
~~~
<?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');
}
}
~~~
現在,當你更新`Comment`時,所屬模型`Post`將也會更新其`updated_at`值:
~~~
$comment = App\Comment::find(1);
$comment->text = 'Edit to this comment!';
$comment->save();
~~~
> 擴展閱讀1:[實例教程 ——?關聯關系及其在模型中的定義(一)](http://laravelacademy.org/post/1095.html)
>
> 擴展閱讀2:[實例教程 —— 關聯關系及其在模型中的定義(二)](http://laravelacademy.org/post/1174.html)
- 前言
- 序言
- 序言 ―― 發行版本說明
- 序言 ―― 升級指南
- 序言 ―― 貢獻代碼
- 開始
- 開始 ―― 安裝及配置
- 開始 ―― Laravel Homestead
- 基礎
- 基礎 ―― HTTP路由
- 基礎 ―― HTTP 中間件
- 基礎 ―― HTTP 控制器
- 基礎 ―― HTTP 請求
- 基礎 ―― HTTP 響應
- 基礎 ―― 視圖
- 基礎 ―― Blade模板
- 架構
- 架構 ―― 一次請求的生命周期
- 架構 ―― 應用目錄結構
- 架構 ―― 服務提供者
- 架構 ―― 服務容器
- 架構 ―― 契約
- 架構 ―― 門面
- 數據庫
- 數據庫 ―― 起步
- 數據庫 ―― 查詢構建器
- 數據庫 ―― 遷移
- 數據庫 ―― 填充數據
- Eloquent ORM
- Eloquent ORM ―― 起步
- Eloquent ORM ―― 關聯關系
- Eloquent ORM ―― 集合
- Eloquent ORM ―― 調整器
- Eloquent ORM ―― 序列化
- 服務
- 服務 ―― 用戶認證
- 服務 ―― Artisan 控制臺
- 服務 ―― Laravel Cashier(交易)
- 服務 ―― 緩存
- 服務 ―― 集合
- 服務 ―― Laravel Elixir
- 服務 ―― 加密
- 服務 ―― 錯誤&日志
- 服務 ―― 事件
- 服務 ―― 文件系統/云存儲
- 服務 ―― 哈希
- 服務 ―― 幫助函數
- 服務 ―― 本地化
- 服務 ―― 郵件
- 服務 ―― 包開發
- 服務 ―― 分頁
- 服務 ―― 隊列
- 服務 ―― Redis
- 服務 ―― Session
- 服務 ―― Envoy 任務運行器(SSH任務)
- 服務 ―― 任務調度
- 服務 ―― 測試
- 服務 ―― 驗證