## 模型關聯
通過模型關聯操作把數據表的關聯關系對象化,解決了大部分常用的關聯場景,封裝的關聯操作比起常規的數據庫聯表操作更加智能和高效,并且直觀。
>[danger] 避免在模型內部使用復雜的`join`查詢和視圖查詢。
從面向對象的角度來看關聯的話,模型的關聯其實應該是模型的某個屬性,比如用戶的檔案關聯,就應該是下面的情況:
~~~
// 獲取用戶模型實例
$user = User::find(1);
// 獲取用戶的檔案
$user->profile;
// 獲取用戶的檔案中的手機資料
$user->profile->mobile;
~~~
為了更方便和靈活的定義模型的關聯關系,框架選擇了方法定義而不是屬性定義的方式,每個**關聯屬性**其實是對應了一個模型的(關聯)方法,這個關聯屬性和模型的數據一樣是動態的,并非模型類的實體屬性。
例如上面的關聯屬性就是在`User`模型類中定義了一個`profile`方法(`mobile`屬性是`Profile`模型的屬性):
~~~
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
~~~
>[danger] 一個模型可以定義多個不同的關聯,增加不同的關聯方法即可
同時,我們必須定義一個`Profile`模型(即使是一個空模型)。
~~~
<?php
namespace app\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; // 建議使用
~~~
>[danger] 推薦關聯屬性統一使用后者,和數據表的字段命名規范一致,因此在很多時候系統自動獲取關聯屬性的時候采用的也是后者。
可以簡單的理解為關聯定義就是在模型類中添加一個方法(注意不要和模型的對象屬性以及其它業務邏輯方法沖突),一般情況下無需任何參數,并在方法中指定一種關聯關系,比如上面的`hasOne`關聯關系,`2.0`版本支持的關聯關系包括下面8種:
|模型方法|關聯類型|
|---|---|
|`hasOne`|一對一|
|`belongsTo`|一對一|
|`hasMany`|一對多 |
|`hasOneThrough`|遠程一對一 |
|`hasManyThrough`|遠程一對多 |
|`belongsToMany`|多對多 |
|`morphMany`|多態一對多 |
|`morphOne`|多態一對一 |
|`morphTo`|多態 |
關聯方法的第一個參數就是要關聯的模型名稱,也就是說當前模型的關聯模型必須也是已經定義好的一個模型。
兩個模型之間因為參照模型的不同就會產生相對的但不一定相同的關聯關系,并且相對的關聯關系只有在需要調用的時候才需要定義,下面是每個關聯類型的相對關聯關系對照:
|類型|關聯關系|相對的關聯關系|
|---|---|---|
|一對一|`hasOne`|`belongsTo`|
|一對多|`hasMany`|`belongsTo`|
|多對多|`belongsToMany`|`belongsToMany`|
|遠程一對多|`hasManyThrough`|`hasOneThrough`|
|多態一對一|`morphOne`|`morphTo`|
|多態一對多|`morphMany`|`morphTo`|
除此之外,關聯定義的幾個要點必須了解:
* 關聯方法必須使用駝峰法命名;
* 關聯方法一般無需定義任何參數;
* 關聯調用的時候駝峰法和小寫+下劃線都支持;
* 關聯字段設計盡可能按照規范可以簡化關聯定義;
* 關聯方法定義可以添加額外查詢條件;
例如,`Profile`模型中就可以定義一個相對的關聯關系。
~~~
<?php
namespace app\model;
use think\Model;
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
~~~
在進行關聯查詢的時候,也是類似,只是當前模型不同。
~~~
// 獲取檔案實例
$profile = Profile::find(1);
// 獲取檔案所屬的用戶名稱
echo $profile->user->name;
~~~
如果是數據集查詢的話,關聯獲取的用法如下:
~~~
// 獲取檔案實例
$profiles = Profile::where('id', '>', 1)->select();
foreach($profiles as $profile) {
// 獲取檔案所屬的用戶名稱
echo $profile->user->name;
}
~~~
如果你需要對關聯模型進行更多的查詢約束,可以在關聯方法的定義方法后面追加額外的查詢鏈式方法(但切忌不要濫用,并且不要使用實際的查詢方法),例如:
~~~
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function book()
{
return $this->hasMany(Book::class)->order('pub_time');
}
}
~~~
模型關聯的優勢主要在于查詢,關聯寫入的實現和用單獨的模型完成區別并不大。
- 簡介
- 數據庫配置
- 分布式數據庫
- 查詢構造器
- 查詢數據
- 新增數據
- 更新數據
- 刪除數據
- 鏈式操作
- Where
- Table
- Alias
- Field
- Strict
- Limit
- Page
- Order
- Group
- Having
- Join
- Union
- Distinct
- Lock
- Cache
- Comment
- FetchSql
- Force
- Partition
- Replace
- FailException
- Extra
- Duplicate
- Sequence
- Procedure
- View
- 聚合查詢
- 分頁查詢
- 時間查詢
- 高級查詢
- 子查詢
- 原生查詢
- 事務操作
- 存儲過程
- 查詢事件
- JSON字段
- 模型
- 定義
- 新增
- 更新
- 刪除
- 查詢
- 查詢范圍
- 只讀字段
- JSON字段
- 自動時間寫入
- 獲取器
- 修改器
- 搜索器
- 類型轉換
- 模型輸出
- 模型事件
- 虛擬模型
- 關聯
- 一對一關聯
- 一對多關聯
- 遠程一對多
- 遠程一對一
- 多對多關聯
- 多態一對多
- 多態一對一
- 關聯預載入
- 關聯統計
- 關聯輸出
- SQL監聽
- 緩存機制
- 字段緩存
- 查詢緩存
- 擴展
- 自定義查詢類
- 自定義數據庫驅動