當然,您的數據庫表很可能跟另一張表相關聯。例如,一篇 blog 文章可能有很多評論,或是一張訂單跟下單客戶相關聯。 Eloquent 讓管理和處理這些關聯變得很容易。 Laravel 有很多種關聯類型:
* 一對一
* 一對多
* 多對多
* 遠層一對多關聯
* 多態關聯
* 多態的多對多關聯
## 一對一
#### 定義一對一關聯
一對一關聯是很基本的關聯。例如一個 User 模型會對應到一個 Phone 。 在 Eloquent 里可以像下面這樣定義關聯:
~~~
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
~~~
傳到 hasOne 方法里的第一個參數是關聯模型的類名稱。定義好關聯之后,就可以使用 Eloquent 的動態屬性取得關聯對象:
~~~
$phone = User::find(1)->phone;
~~~
SQL 會執行如下語句:
~~~
select * from users where id = 1
select * from phones where user_id = 1
~~~
> 注意, Eloquent 假設對應的關聯模型數據庫表里,外鍵名稱是基于模型名稱。在這個例子里,默認 Phone 模型數據庫表會以 user_id 作為外鍵。如果想要更改這個默認,可以傳入第二個參數到 hasOne 方法里。更進一步,您可以傳入第三個參數,指定關聯的外鍵要對應到本身的哪個字段:
~~~
return $this->hasOne('App\Phone', 'foreign_key');
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
~~~
## 定義相對的關聯
要在 Phone 模型里定義相對的關聯,可以使用 belongsTo 方法:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
~~~
在上面的例子里, Eloquent 默認會使用 phones 數據庫表的 user_id 字段查詢關聯。如果想要自己指定外鍵字段,可以在 belongsTo 方法里傳入第二個參數:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User', 'local_key');
}
}
~~~
除此之外,也可以傳入第三個參數指定要參照上層數據庫表的哪個字段:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User', 'local_key', 'parent_key');
}
}
~~~
## 一對多
一對多關聯的例子如,一篇 Blog 文章可能「有很多」評論。可以像這樣定義關聯:
~~~
class Post extends Model {
public function comments()
{
return $this->hasMany('App\Comment');
}
}
~~~
現在可以經由動態屬性取得文章的評論:
~~~
$comments = Post::find(1)->comments;
~~~
如果需要增加更多條件限制,可以在調用 comments 方法后面通過鏈式查詢條件方法:
~~~
$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();
~~~
同樣的,您可以傳入第二個參數到 hasMany 方法更改默認的外鍵名稱。以及,如同 hasOne 關聯,可以指定本身的對應字段:
~~~
return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
~~~
## 定義相對的關聯
要在 Comment 模型定義相對應的關聯,可使用 belongsTo 方法:
~~~
class Comment extends Model {
public function post()
{
return $this->belongsTo('App\Post');
}
}
~~~
## 多對多
多對多關聯更為復雜。這種關聯的例子如,一個用戶( user )可能用有很多身份( role ),而一種身份可能很多用戶都有。例如很多用戶都是「管理者」。多對多關聯需要用到三個數據庫表: users , roles ,和 role_user 。 role_user 樞紐表命名是以相關聯的兩個模型數據庫表,依照字母順序命名,樞紐表里面應該要有 user_id 和 role_id 字段。
可以使用 belongsToMany 方法定義多對多關系:
~~~
class User extends Model {
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
~~~
現在我們可以從 User 模型取得 roles:
~~~
$roles = User::find(1)->roles;
~~~
如果不想使用默認的樞紐數據庫表命名方式,可以傳遞數據庫表名稱作為 belongsToMany 方法的第二個參數:
~~~
return $this->belongsToMany('App\Role', 'user_roles');
~~~
也可以更改默認的關聯字段名稱:
~~~
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
~~~
當然,也可以在 Role 模型定義相對的關聯:
~~~
class Role extends Model {
public function users()
{
return $this->belongsToMany('App\User');
}
}
~~~
## Has Many Through 遠層一對多關聯
「遠層一對多關聯」提供了方便簡短的方法,可以經由多層間的關聯取得遠層的關聯。例如,一個 Country 模型可能通過 Users 關聯到很多 Posts 模型。 數據庫表間的關系可能看起來如下:
|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 取得 country 的 posts。我們可以定義以下關聯:
~~~
class Country extends Model {
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
}
~~~
如果想要手動指定關聯的字段名稱,可以傳入第三和第四個參數到方法里:
~~~
class Country extends Model {
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
}
}
~~~
## 多態關聯
多態關聯可以用一個簡單的關聯方法,就讓一個模型同時關聯多個模型。例如,您可能想讓 photo 模型同時和一個 staff 或 order 模型關聯。可以定義關聯如下:
~~~
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');
}
}
~~~
## 取得多態關聯對象
現在我們可以從 staff 或 order 模型取得多態關聯對象:
~~~
$staff = Staff::find(1);
foreach ($staff->photos as $photo)
{
//
}
~~~
## 取得多態關聯對象的擁有者
然而,多態關聯真正神奇的地方,在于要從 Photo 模型取得 staff 或 order 對象時:
~~~
$photo = Photo::find(1);
$imageable = $photo->imageable;
~~~
Photo 模型里的 imageable 關聯會返回 Staff 或 Order 實例,取決于這是哪一種模型擁有的照片。
多態關聯的數據庫表結構
為了理解多態關聯的運作機制,來看看它們的數據庫表結構:
|staff|
|-----|
|id - integer|
|name - string|
|orders|
|-----|
|id - integer|
|price - integer|
|photos|
|-----|
|id - integer|
|path - string|
|imageable_id - integer|
|imageable_type - string|
要注意的重點是 photos 數據庫表的 imageable_id 和 imageable_type。在上面的例子里, ID 字段會包含 staff 或 order 的 ID,而 type 是擁有者的模型類名稱。這就是讓 ORM 在取得 imageable 關聯對象時,決定要哪一種模型對象的機制。
## 多態的多對多關聯
Polymorphic Many To Many Relation Table Structure 多態的多對多關聯數據庫表結構
除了一般的多態關聯,也可以使用多對多的多態關聯。例如,Blog 的 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|
現在,我們準備好設定模型關聯了。 Post 和 Video 模型都可以經由 tags 方法建立 morphToMany 關聯:
~~~
class Post extends Model {
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
~~~
在 Tag 模型里針對每一種關聯建立一個方法:
~~~
class Tag extends Model {
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
}
~~~
- 前言
- 發行說明/L5新特性
- 升級向導
- 升級到 5.0.16
- 從 4.2 升級到 5.0
- 從 4.1 升級到 4.2
- 從 4.1.x 升級到 4.1.29
- 從 4.1.25 升級到 4.1.26
- 從 4.0 升級到 4.1
- 貢獻向導
- 環境配置
- 安裝
- 配置
- 基本功能
- 路由
- 基本路由
- CSRF 保護
- 方法欺騙
- 路由參數
- 命名路由
- 路由群組
- 路由模型綁定
- 拋出 404 錯誤
- 中間件
- 建立中間件
- 注冊中間件
- 可終止中間件
- 控制器
- 基礎控制器
- 控制器中間件
- 隱式控制器
- RESTful 資源控制器
- 請求
- 取得請求實例
- 取得輸入數據
- 舊輸入數據
- Cookies
- 上傳文件
- 其他的請求信息
- 響應
- 基本響應
- 重定向
- 其他響應
- 響應宏
- 系統架構
- 服務提供者
- 基本提供者例子
- 注冊提供者
- 緩載提供者
- 服務容器
- 基本用法
- 將接口綁定到實現
- 上下文綁定
- 標簽
- 實際應用
- 容器事件
- 參考:理解PHP 依賴注入|Laravel IoC容器
- Contracts
- 為什么用 Contracts
- Contract 參考
- 如何使用 Contracts
- Facades
- 實際用法
- 建立 Facades
- 模擬 Facades
- Facade 類參考
- 請求的生命周期
- 生命周期概要
- 聚焦于服務提供者
- 應用程序結構
- 根目錄
- App 目錄
- 為應用程序配置命名空間
- 系統服務
- 認證
- 用戶認證
- 取得經過認證的用戶
- 保護路由
- HTTP 基本認證
- 忘記密碼與重設
- 第三方登陸認證
- 交易
- 配置文件
- 訂購方案
- 一次性付款
- Single Charges
- 免信用卡試用
- 訂購轉換
- 訂購數量
- 取消訂購
- 恢復訂購
- 確認訂購狀態
- 處理失敗訂閱
- 處理其它 Stripe Webhooks
- 收據
- 緩存
- 配置
- 緩存用法
- 遞增與遞減
- 緩存標簽
- 緩存事件
- 數據庫緩存
- 集合
- Command Bus
- 建立命令
- 調用命令
- 命令隊列
- 命令管道
- 核心擴展
- 管理者和工廠
- 緩存
- Session
- 認證
- 基于服務容器的擴展
- Laravel Elixir
- 安裝與配置
- 使用方式
- Gulp
- Custom Tasks and Extensions
- 加密
- Envoy 任務執行器
- 安裝
- 執行任務
- 多服務器
- 并行執行
- 任務宏
- 通知
- 更新 Envoy
- 錯誤與日志
- 配置
- 錯誤處理
- HTTP 異常
- 日志
- 事件
- 基本用法
- 事件處理隊列
- 事件訂閱者
- 文件系統與云存儲
- 配置文件
- 基本用法
- 自定義文件系統
- 哈希
- 基本用法
- 輔助方法
- 數組
- 路徑
- 路由
- 字符串
- 網址(URL)
- 其他
- 本地化
- 語言文件
- 基本用法
- 復數
- 驗證
- 覆寫擴展包的語言文件
- 郵件
- 配置
- 基本用法
- 內嵌附件
- 郵件隊列
- 郵件與本地端開發
- 擴展包開發
- 視圖
- 語言
- 配置文件
- 公共資源
- 發布分類文件
- 路由
- 分頁
- 配置
- 使用
- 追加分頁鏈接
- 轉換至 JSON
- 隊列
- 設置
- 基本用法
- 隊列閉包
- 執行一個隊列監聽
- 常駐隊列處理器
- 推送隊列
- 已失敗的工作
- 會話
- 配置
- 使用 Session
- 暫存數據(Flash Data)
- 數據庫 Sessions
- Session 驅動
- 模板
- Blade 模板
- Blade 控制語法結構
- Blade 擴展
- 參考:@section與@yield 介紹
- 單元測試
- 定義并執行測試
- 測試環境
- 從測試調用路由
- 模擬 Facades
- 框架 Assertions
- 輔助方法
- 重置應用程序
- 表單驗證
- 基本用法
- 控制器驗證
- 表單請求驗證
- 使用錯誤信息
- 錯誤信息 & 視圖
- 可用驗證規則
- 條件驗證規則
- 自定義錯誤信息
- 自定義驗證規則
- 數據庫
- 使用基礎
- 配置
- 讀取/寫入連接
- 執行查找
- 數據庫事務處理
- 獲取連接
- 日志記錄
- 查詢構造器
- Selects
- Joins
- 高級 Wheres
- 聚合
- 原生表達式
- 添加
- 更新
- 刪除
- Unions
- 悲觀鎖定 (Pessimistic Locking)
- Eloquent ORM
- 基本用法
- 批量賦值
- 新增,更新,刪除
- 軟刪除
- 時間戳
- 范圍查詢
- Global Scopes
- 關聯
- 關聯查詢
- 預載入
- 新增關聯模型
- 更新上層時間戳
- 使用樞紐表
- 集合
- 獲取器和修改器
- 日期轉換器
- 屬性類型轉換
- 模型事件
- 模型觀察者
- 模型 URL 生成
- 轉換成數組 / JSON
- 結構生成器
- 建立與刪除數據表
- 加入字段
- 修改字段
- 修改字段名稱
- 移除字段
- 檢查是否存在
- 加入索引
- 外鍵
- 移除索引
- 移除時間戳記和軟刪除
- 保存引擎
- 遷移和數據填充
- 建立遷移文件
- 執行遷移
- 回滾遷移
- 數據填充
- Redis
- 配置
- 使用方式
- 管道
- 開發包
- Confide 用戶身份認證
- Entrust 權限管理
- Shoppingcart 購物車
- Genertators 代碼生成工具
- IDE Helper IDE助手
- Artisan 命令行工具
- 概覽
- 用法
- 在命令行接口以外的地方調用命令
- 定時調用 Artisan 命令
- 開發
- 建立自定義命令
- 注冊自定義命令