# 模型關聯
## 模型關聯
通過模型關聯操作把數據表的關聯關系對象化,解決了大部分常用的關聯場景,封裝的關聯操作比起常規的數據庫聯表操作更加智能和高效,并且直觀。
> 避免在模型內部使用復雜的`join`查詢和視圖查詢。
從面向對象的角度來看關聯的話,模型的關聯其實應該是模型的某個屬性,比如用戶的檔案關聯,就應該是下面的情況:
```
// 獲取用戶模型實例
$user = User::get(1);
// 獲取用戶的檔案
$user->profile;
// 獲取用戶的檔案中的手機資料
$user->profile->mobile;
```
為了更方便和靈活的定義模型的關聯關系,框架選擇了方法定義而不是屬性定義的方式,每個**關聯屬性**其實是對應了一個模型的(關聯)方法,這個關聯屬性和模型的數據一樣是動態的,并非模型類的實體屬性。
例如上面的關聯屬性就是在`User`模型類中定義了一個`profile`方法(`mobile`屬性是`Profile`模型的屬性):
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function profile()
{
return $this->hasOne('Profile');
}
}
```
> 一個模型可以定義多個不同的關聯,增加不同的關聯方法即可
同時,我們必須定義一個`Profile`模型(即使是一個空模型)。
```
<?php
namespace app\index\model;
use think\Model;
class Profile extends Model
{
}
```
關聯方法返回的是不同的關聯對象,例如這里的`profile`方法返回的是一個`HasOne`關聯對象(`think\model\relation\HasOne`)實例。
當我們訪問`User`模型對象實例的`profile`屬性的時候,其實就是調用了`profile`方法來完成關聯查詢。
按照`PSR-2`規范,模型的方法名都是駝峰命名的,所以系統做了一個兼容處理,如果我們定義了一個`userProfile`的關聯方法的時候,在獲取關聯屬性的時候,下面兩種方式都是有效的:
```
$user->userProfile;
$user->user_profile;
```
> 推薦關聯屬性統一使用后者,和數據表的字段命名規范一致,因此在很多時候系統自動獲取關聯屬性的時候采用的也是后者。
可以簡單的理解為關聯定義就是在模型類中添加一個方法(注意不要和模型的對象屬性以及其它業務邏輯方法沖突),一般情況下無需任何參數,并在方法中指定一種關聯關系,比如上面的`hasOne`關聯關系,`5.1`版本支持的關聯關系包括下面8種,后面會給大家陸續介紹:
模型方法關聯類型`hasOne`一對一`belongsTo`一對一`hasMany`一對多`hasManyThrough`遠程一對多`belongsToMany`多對多`morphMany`多態一對多`morphOne`多態一對一`morphTo`多態關聯方法的第一個參數就是要關聯的模型名稱,也就是說當前模型的關聯模型必須也是已經定義好的一個模型。
一般不需要使用命名空間,會自動使用當前模型的命名空間,如果不同請使用完整命名空間定義,例如:
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function profile()
{
// Profile模型和當前模型的命名空間不一致
return $this->hasOne('app\model\Profile');
}
}
```
兩個模型之間因為參照模型的不同就會產生相對的但不一定相同的關聯關系,并且相對的關聯關系只有在需要調用的時候才需要定義,下面是每個關聯類型的相對關聯關系對照:
類型關聯關系相對的關聯關系一對一`hasOne``belongsTo`一對多`hasMany``belongsTo`多對多`belongsToMany``belongsToMany`遠程一對多`hasManyThrough`不支持多態一對一`morphOne``morphTo`多態一對多`morphMany``morphTo`例如,`Profile`模型中就可以定義一個相對的關聯關系。
```
<?php
namespace app\index\model;
use think\Model;
class Profile extends Model
{
public function user()
{
return $this->belongsTo('User');
}
}
```
在進行關聯查詢的時候,也是類似,只是當前模型不同。
```
// 獲取檔案實例
$profile = Profile::get(1);
// 獲取檔案所屬的用戶名稱
echo $profile->user->name;
```
如果你需要對關聯模型進行更多的查詢約束,可以在關聯方法的定義方法后面追加額外的查詢鏈式方法(但切忌不要濫用,并且不要使用實際的查詢方法),例如:
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function book()
{
return $this->hasMany('Book')->order('pub_time');
}
}
```
> 5\.1版本的模型關聯支持調用模型的方法
> 具體不同的關聯關系的詳細使用,請繼續參考后面的內容。
- 序言
- 基礎
- 安裝
- 開發規范
- 目錄結構
- 配置
- 架構
- 架構總覽
- 入口文件
- URL訪問
- 模塊設計
- 命名空間
- 容器和依賴注入
- Facade
- 鉤子和行為
- 中間件
- 路由
- 路由定義
- 變量規則
- 路由地址
- 閉包支持
- 路由參數
- 路由緩存
- 跨域請求
- 注解路由
- 路由分組
- MISS路由
- 資源路由
- 快捷路由
- 路由別名
- 路由綁定
- 域名路由
- URL生成
- 控制器
- 控制器定義
- 前置操作
- 跳轉和重定向
- 空操作和空控制器
- 分層控制器
- 資源控制器
- 請求
- 請求對象
- 請求信息
- 輸入變量
- 請求類型
- HTTP頭信息
- 偽靜態
- 參數綁定
- 請求緩存
- 響應
- 響應輸出
- 響應參數
- 重定向
- 數據庫
- 連接數據庫
- 查詢構造器
- 查詢數據
- 添加數據
- 更新數據
- 刪除數據
- 查詢表達式
- 鏈式操作
- 聚合查詢
- 時間查詢
- 高級查詢
- 視圖查詢
- JSON字段
- 子查詢
- 原生查詢
- 查詢事件
- 事務操作
- 監聽SQL
- 存儲過程
- 數據集
- 分布式數據庫
- 模型
- 定義
- 新增
- 更新
- 刪除
- 查詢
- JSON字段
- 獲取器
- 修改器
- 自動時間戳
- 只讀字段
- 軟刪除
- 類型轉換
- 數據完成
- 查詢范圍
- 模型輸出
- 模型事件
- 模型關聯
- 一對一關聯
- 一對多關聯
- 遠程一對多
- 多對多關聯
- 多態關聯
- 關聯預載入
- 關聯統計
- 關聯輸出
- 視圖
- 視圖渲染
- 視圖賦值
- 視圖過濾
- 模板引擎
- 模板
- 變量輸出
- 使用函數
- 運算符
- 原樣輸出
- 模板注釋
- 模板布局
- 模板繼承
- 包含文件
- 輸出替換
- 標簽庫
- 內置標簽
- 循環標簽
- 比較標簽
- 條件判斷
- 資源文件加載
- 標簽嵌套
- 原生PHP
- 定義標簽
- 標簽擴展
- 錯誤和日志
- 異常處理
- 日志處理
- 調試
- 調試模式
- Trace調試
- 性能調試
- SQL調試
- 變量調試
- 遠程調試
- 驗證
- 驗證器
- 驗證規則
- 錯誤信息
- 驗證場景
- 路由驗證
- 內置規則
- 獨立驗證
- 靜態調用
- 表單令牌
- 雜項
- 緩存
- Session
- Cookie
- 多語言
- 分頁
- 上傳
- 命令行
- 啟動內置服務器
- 自動生成目錄結構
- 創建類庫文件
- 生成類庫映射文件
- 清除緩存文件
- 生成配置緩存文件
- 生成數據表字段緩存
- 生成路由映射緩存
- 自定義指令
- 擴展庫
- 驗證碼
- 圖像處理
- Time
- 數據庫遷移工具
- Workerman
- MongoDb
- 單元測試
- 安全和性能
- 安全建議
- 優化建議
- 附錄
- 助手函數
- 升級指導
- 更新日志