關(guān)聯(lián)

2018-02-24 15:52 更新

當(dāng)然,您的數(shù)據(jù)庫表很可能跟另一張表相關(guān)聯(lián)。例如,一篇 blog 文章可能有很多評論,或是一張訂單跟下單客戶相關(guān)聯(lián)。 Eloquent 讓管理和處理這些關(guān)聯(lián)變得很容易。 Laravel 有很多種關(guān)聯(lián)類型:

  • 一對一
  • 一對多
  • 多對多
  • 遠(yuǎn)層一對多關(guān)聯(lián)
  • 多態(tài)關(guān)聯(lián)
  • 多態(tài)的多對多關(guān)聯(lián)

一對一

定義一對一關(guān)聯(lián)

一對一關(guān)聯(lián)是很基本的關(guān)聯(lián)。例如一個(gè) User 模型會(huì)對應(yīng)到一個(gè) Phone 。 在 Eloquent 里可以像下面這樣定義關(guān)聯(lián):

class User extends Model {
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

傳到 hasOne 方法里的第一個(gè)參數(shù)是關(guān)聯(lián)模型的類名稱。定義好關(guān)聯(lián)之后,就可以使用 Eloquent 的動(dòng)態(tài)屬性取得關(guān)聯(lián)對象:

$phone = User::find(1)->phone;

SQL 會(huì)執(zhí)行如下語句:

select * from users where id = 1
select * from phones where user_id = 1

注意, Eloquent 假設(shè)對應(yīng)的關(guān)聯(lián)模型數(shù)據(jù)庫表里,外鍵名稱是基于模型名稱。在這個(gè)例子里,默認(rèn) Phone 模型數(shù)據(jù)庫表會(huì)以 user_id 作為外鍵。如果想要更改這個(gè)默認(rèn),可以傳入第二個(gè)參數(shù)到 hasOne 方法里。更進(jìn)一步,您可以傳入第三個(gè)參數(shù),指定關(guān)聯(lián)的外鍵要對應(yīng)到本身的哪個(gè)字段:

return $this->hasOne('App\Phone', 'foreign_key');
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

定義相對的關(guān)聯(lián)

要在 Phone 模型里定義相對的關(guān)聯(lián),可以使用 belongsTo 方法:

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

在上面的例子里, Eloquent 默認(rèn)會(huì)使用 phones 數(shù)據(jù)庫表的 user_id 字段查詢關(guān)聯(lián)。如果想要自己指定外鍵字段,可以在 belongsTo 方法里傳入第二個(gè)參數(shù):

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User', 'local_key');
    }
}

除此之外,也可以傳入第三個(gè)參數(shù)指定要參照上層數(shù)據(jù)庫表的哪個(gè)字段:

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User', 'local_key', 'parent_key');
    }
}

一對多

一對多關(guān)聯(lián)的例子如,一篇 Blog 文章可能「有很多」評論。可以像這樣定義關(guān)聯(lián):

class Post extends Model {
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

現(xiàn)在可以經(jīng)由動(dòng)態(tài)屬性取得文章的評論:

$comments = Post::find(1)->comments;

如果需要增加更多條件限制,可以在調(diào)用 comments 方法后面通過鏈?zhǔn)讲樵儣l件方法:

$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();

同樣的,您可以傳入第二個(gè)參數(shù)到 hasMany 方法更改默認(rèn)的外鍵名稱。以及,如同 hasOne 關(guān)聯(lián),可以指定本身的對應(yīng)字段:

return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');

定義相對的關(guān)聯(lián)

要在 Comment 模型定義相對應(yīng)的關(guān)聯(lián),可使用 belongsTo 方法:

class Comment extends Model {
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

多對多

多對多關(guān)聯(lián)更為復(fù)雜。這種關(guān)聯(lián)的例子如,一個(gè)用戶( user )可能用有很多身份( role ),而一種身份可能很多用戶都有。例如很多用戶都是「管理者」。多對多關(guān)聯(lián)需要用到三個(gè)數(shù)據(jù)庫表: users , roles ,和 role_user 。 role_user 樞紐表命名是以相關(guān)聯(lián)的兩個(gè)模型數(shù)據(jù)庫表,依照字母順序命名,樞紐表里面應(yīng)該要有 user_id 和 role_id 字段。

可以使用 belongsToMany 方法定義多對多關(guān)系:

class User extends Model {
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

現(xiàn)在我們可以從 User 模型取得 roles:

$roles = User::find(1)->roles;

如果不想使用默認(rèn)的樞紐數(shù)據(jù)庫表命名方式,可以傳遞數(shù)據(jù)庫表名稱作為 belongsToMany 方法的第二個(gè)參數(shù):

return $this->belongsToMany('App\Role', 'user_roles');

也可以更改默認(rèn)的關(guān)聯(lián)字段名稱:

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');

當(dāng)然,也可以在 Role 模型定義相對的關(guān)聯(lián):

class Role extends Model {
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

Has Many Through 遠(yuǎn)層一對多關(guān)聯(lián)

「遠(yuǎn)層一對多關(guān)聯(lián)」提供了方便簡短的方法,可以經(jīng)由多層間的關(guān)聯(lián)取得遠(yuǎn)層的關(guān)聯(lián)。例如,一個(gè) Country 模型可能通過 Users 關(guān)聯(lián)到很多 Posts 模型。 數(shù)據(jù)庫表間的關(guān)系可能看起來如下:

countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string

雖然 posts 數(shù)據(jù)庫表本身沒有 country_id 字段,但 hasManyThrough 方法讓我們可以使用 $country->posts 取得 country 的 posts。我們可以定義以下關(guān)聯(lián):

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

如果想要手動(dòng)指定關(guān)聯(lián)的字段名稱,可以傳入第三和第四個(gè)參數(shù)到方法里:

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
    }
}

多態(tài)關(guān)聯(lián)

多態(tài)關(guān)聯(lián)可以用一個(gè)簡單的關(guān)聯(lián)方法,就讓一個(gè)模型同時(shí)關(guān)聯(lián)多個(gè)模型。例如,您可能想讓 photo 模型同時(shí)和一個(gè) staff 或 order 模型關(guān)聯(lián)??梢远x關(guān)聯(lián)如下:

class Photo extends Model {

    public function imageable()
    {
        return $this->morphTo();
    }

}
class Staff extends Model {

    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }

}

class Order extends Model {

    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }

}

取得多態(tài)關(guān)聯(lián)對象

現(xiàn)在我們可以從 staff 或 order 模型取得多態(tài)關(guān)聯(lián)對象:

$staff = Staff::find(1);
foreach ($staff->photos as $photo)
{
    //
}

取得多態(tài)關(guān)聯(lián)對象的擁有者

然而,多態(tài)關(guān)聯(lián)真正神奇的地方,在于要從 Photo 模型取得 staff 或 order 對象時(shí):

$photo = Photo::find(1);
$imageable = $photo->imageable;

Photo 模型里的 imageable 關(guān)聯(lián)會(huì)返回 Staff 或 Order 實(shí)例,取決于這是哪一種模型擁有的照片。
多態(tài)關(guān)聯(lián)的數(shù)據(jù)庫表結(jié)構(gòu)

為了理解多態(tài)關(guān)聯(lián)的運(yùn)作機(jī)制,來看看它們的數(shù)據(jù)庫表結(jié)構(gòu):

staff
id - integer
name - string
orders
id - integer
price - integer
photos
id - integer
path - string
imageable_id - integer
imageable_type - string

要注意的重點(diǎn)是 photos 數(shù)據(jù)庫表的 imageable_id 和 imageable_type。在上面的例子里, ID 字段會(huì)包含 staff 或 order 的 ID,而 type 是擁有者的模型類名稱。這就是讓 ORM 在取得 imageable 關(guān)聯(lián)對象時(shí),決定要哪一種模型對象的機(jī)制。

多態(tài)的多對多關(guān)聯(lián)

Polymorphic Many To Many Relation Table Structure 多態(tài)的多對多關(guān)聯(lián)數(shù)據(jù)庫表結(jié)構(gòu)

除了一般的多態(tài)關(guān)聯(lián),也可以使用多對多的多態(tài)關(guān)聯(lián)。例如,Blog 的 Post 和 Video 模型可以共用多態(tài)的 Tag 關(guān)聯(lián)模型。首先,來看看數(shù)據(jù)庫表結(jié)構(gòu):

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

現(xiàn)在,我們準(zhǔn)備好設(shè)定模型關(guān)聯(lián)了。 Post 和 Video 模型都可以經(jīng)由 tags 方法建立 morphToMany 關(guān)聯(lián):

class Post extends Model {
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

在 Tag 模型里針對每一種關(guān)聯(lián)建立一個(gè)方法:

class Tag extends Model {
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }
    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號