一、序言
最近在寫ThinkPHP關聯模型的時候一些用法總忘,我就想透過寫部落格的方式複習和整理下一些用法。
具體版本:
- topthink/framework:6.1.4
- topthink/think-orm:2.0.61
二、例項應用
1、一對一關聯
1.1、我先設計了兩張表,分別為使用者表(user),使用者擴充套件表(user_extend)
1.2、分別給兩個表建立模型
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2023/12/19 * Time: 14:50 */ namespace app\common\model; /** * 使用者模型 */ class UserModel extends ComBaseModel { protected $name='user'; /** * 關聯的使用者擴充套件表 * hasOne的第一個引數是要關聯的模型類名,第二個引數是關聯的外來鍵名,第三個引數是當前模型(userModel)的主鍵名 * @return \think\model\relation\HasOne * @Author: fengzi * @Date: 2024/6/27 17:38 */ public function userExtend() { return $this->hasOne(UserExtendModel::class,'user_id','id'); } }
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2023/12/19 * Time: 14:50 */ namespace app\common\model; /** * 使用者擴充套件表 */ class UserExtendModel extends ComBaseModel { protected $name='user_extend'; /** * 使用者模型的相對關聯 * belongsTo的第一個引數是關聯模型類名,第二個引數是當前模型(UserExtendModel)的外來鍵,第三個引數是關聯表的主鍵 * @return \think\model\relation\BelongsTo * @Author: fengzi * @Date: 2024/6/27 17:41 */ public function user() { return $this->belongsTo(UserModel::class,'user_id','id'); } }
1.3、with() 關聯查詢
一對一關聯查詢,user表對user_extend表使用的hasOne,使用hasWhere查詢user_extend表時,相當於把user_extend表中符合查詢條件的user_id集合作為user表的查詢條件。
因為使用的一對一關聯,所以當user_extend沒找到對應查詢資料時,user表也不會有資料。
注意:
- hasWhere() 的第一個引數一定是user模型中關聯使用者擴充套件表的方法名稱。
- with預載入查詢中的關聯模型中的名稱是user模型中關聯使用者擴充套件表的方法名稱,如果方法名是駝峰寫法,也可以轉換成下劃線。如:userExtend => user_extend
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/6/26 * Time: 17:13 */ namespace app\admin\controller\orm; use app\common\model\UserModel; class OrmController { /** * @var UserModel|object|\think\App */ private UserModel $userModel; public function __construct() { $this->userModel = app(UserModel::class); } public function index() { /** * with 關聯查詢一對一 * hasWhere第四個引數joinType的值:LEFT、RIGHT 或 INNER * user表對user_extend表使用的hasOne * 使用hasWhere查詢user_extend表時,相當於把user_extend表中符合查詢條件的user_id集合作為user表的查詢條件。 * 因為使用的一對一關聯,所以當user_extend沒找到對應查詢資料時,user表也不會有資料 */ $lists = $this->userModel ->with(['user_extend']) ->hasWhere('userExtend', function ($query){ $query->where('organize_id', 1); }, '*', 'LEFT') ->select()->toArray(); // 輸出列印 dd($lists); } }
1.3.1、查詢結果展示
1.4、withJoin() 的查詢( 我在user模型中定義的user_extend表的關聯方法名稱為userExtend ):
- withJoin(['user_extend”]):這種寫法查詢出來的關聯表資料會和主表資料在同一層級。而關聯表資料的展現方式會以user模型中定義的關聯方法名稱為字首+下劃線+表欄位的形式呈現。
- 注意:【userExtend__xxx】中的下劃線是兩個,並不是一個下劃線(一個下劃線 _ ;兩個下劃線 __ )。所以在查詢結果物件轉換成陣列後去獲取欄位資料時要注意,避免報錯。
- 這個寫法同級中還多了一個【user_extend】欄位,其值為null。這個欄位在兩個資料表中都是不存在的。
- withJoin(['userExtend”]):這種寫法査詢出來的關聯表資料與主表資料是有父子級關係。以withJoin中定義的模型名稱“userExtend”作為主表的一個欄位,把關聯表的資料放入這個欄位中。
public function index() { // 例項化模型 $userModel = app(UserModel::class); /** * 聯表資料和主表資料在同一級 */ $info = $userModel->withJoin(['user_extend'])->find(1); dump($info->toArray()); /** * 聯表資料是主表資料的一個子級,主/聯表資料呈現父子級關係 */ $info = $userModel->withJoin(['userExtend'])->find(1); dd($info->toArray()); }
1.4.1、withJoin的條件查詢注意事項:
- 使用 withJoin() 做關聯,進行條件查詢的時候,只要使用 where() 方法進行條件篩選即可。
- 如果主/聯表中有相同的欄位且篩選條件就是相同的欄位,使用 hasWhere() 方法進行條件篩選會報錯。
- 如果主/聯表中有相同的欄位且篩選條件是主表中的欄位,那麼where條件應該這麼寫【where(['mkl_user.role_id'=>2])】。同時引數中的role_id要寫明是哪個表,且帶上表字首。
- 如果主/聯表中有相同的欄位且篩選條件是聯表中的欄位,那麼where條件應該這麼寫【where(['userExtend.role_id'=>3])】。同時引數中的role_id要寫明是哪個關聯表,關聯名稱跟【withJoin('userExtend')】的一樣。
public function index() { // 例項化模型 $userModel = app(UserModel::class); /** * where(['organize_id'=>3]) * 可以根據條件查詢出結果 * * * hasWhere('userExtend', function ($query){ * $query->where('organize_id', 3); * }, '*', 'LEFT') * 不能查詢出結果,報錯“Illegal offset type” */ $info = $userModel->withJoin(['user_extend']) ->where(['organize_id'=>3]) /*->hasWhere('user_extend', function ($query){ $query->where('organize_id', 3); })*/ ->select(); /*********** 以下是主/聯表有相同欄位(role_id欄位相同)時的查詢方式 ***********/
// 要篩選主表(user表)中的相同欄位時 $info = $userModel ->withJoin('userExtend') ->where(['mkl_user.role_id'=>2]) ->select(); // 輸出列印 dump($info->toArray()); // 要篩選關聯表(user_extend表)中的相同欄位時 $extendInfo = $userModel ->withJoin('userExtend') ->where(['userExtend.role_id'=>3]) ->select(); // 輸出列印 dd($extendInfo->toArray()); }
1.5、with 關聯修改
關聯修改的常見形式有兩種:
- 方式一:把關聯表要修改的資料寫入一個陣列中,使用save方法直接修改。
- 方式二:單獨欄位一個個賦值後,最後使用save方法進行修改。
注意:
- 不管是上面哪種,查詢出來的物件不能轉換成陣列後進行關聯表的修改,不然就會報錯。
- with中的關聯名稱一般要與修改時一致。當然,不一致也可以,寫模型中關聯方法的名稱也行。如:with使用user_extend,修改時使用userExtend也是可以的。
public function index() { // 關聯查詢 $info = $this->userModel->with(['user_extend'])->find(1); // 輸出列印 dump($info->toArray()); // 關聯修改:方式一 /*$userExtend = [ 'email' => '88888888@qq.com', 'gender' => 1, ]; $info->user_extend->save($userExtend);*/ // 關聯修改:方式二 $info->userExtend->email = '88888888@qq.com'; $info->userExtend->gender = 0; $info->userExtend->save(); // 再次關聯查詢 $newInfo = $this->userModel->with(['user_extend'])->find(1); // 輸出列印 dd($newInfo->toArray()); }
1.5.1、with關聯修改後的結果
1.6、關聯刪除
關聯刪除這裡有一些小細節需要注意,下面我們用user_id=1的使用者來試驗。下圖user_id=1的使用者資料目前是沒有刪除的。
1.6.1、with 的關聯刪除
(1)這種寫法能正確的刪除主表和關聯表的資料,需要注意的是【 with(['userExtend']) 】和【 together(['userExtend']) 】中的引數名稱要一樣,不能一個駝峰【userExtend】寫法,另一個下劃線【user_extend】寫法。要麼都寫【userExtend】,要麼都寫【user_extend】。
public function index() { // 例項化模型 $userModel = app(UserModel::class); // 查詢使用者ID=1的資料 $info = $userModel->with(['userExtend'])->find(1); // 刪除主表和關聯表 $del = $info->together(['userExtend'])->delete(); }
(2)這種寫法在查詢的時候沒有帶 with() 方法,那麼在刪除資料的時候,只能刪除主表資料,關聯表資料不會刪除。
public function index() { // 例項化模型 $userModel = app(UserModel::class); // 查詢使用者ID=1的資料 $info = $userModel->find(1); // 刪除主表和關聯表 $del = $info->together(['userExtend'])->delete(); // 輸出列印 dd($del); }
注意:當主表的資料已經被刪除後,還呼叫 together() 進行主/聯表的資料刪除就會報錯。
(3)當前程式碼中的方式一和方式二其實跟上面介紹的沒什麼區別,只是上面幾個寫法的鏈式操作。
- 方式一:只能刪除主表資料,關聯表資料不會刪除。
- 方式二:可以同時刪除主表和關聯表的資料。
public function index() { // 例項化模型 $userModel = app(UserModel::class);// 刪除主表和關聯表 // 方式一 $del = $userModel->find(1)->together(['userExtend'])->delete(); // 方式二 $del = $userModel->with(['userExtend'])->find(1)->together(['userExtend'])->delete(); }
1.6.2、withJoin 的關聯刪除
注意:
- 如果要主/聯表的資料都刪除,那麼在查詢的時候必須帶 withJoin() 方法。
- 帶 withJoin() 方法查詢後,刪除時 together() 和 withJoin() 方法的引數必須是 UserModel 模型中關聯的方法名稱,即本文1.2處的userExtend() 方法的名稱。
- 如果不帶 withJoin() 方法查詢後,想要刪除主/聯表的資料是不可行的,這時刪除只會刪主表資料,聯表資料是不會刪除。
public function index() { // 例項化模型 $userModel = app(UserModel::class); /** * withJoin(['user_extend']) together(['userExtend']) 主表資料刪除,聯表資料不會刪除 * withJoin(['user_extend']) together(['user_extend']) 主表資料刪除,聯表資料不會刪除 * withJoin(['userExtend']) together(['userExtend']) 主表資料刪除,聯表資料刪除 * withJoin(['userExtend']) together(['user_extend']) 主表資料刪除,聯表資料不會刪除 */ // 查詢使用者ID=1的資料 $info = $userModel->withJoin(['userExtend'])->find(1); // 刪除主表和關聯表 $del = $info->together(['userExtend'])->delete();
/** * together(['user_extend']) 主表資料刪除,聯表資料不會刪除 * together(['userExtend']) 主表資料刪除,聯表資料不會刪除 */ // 查詢使用者ID=1的資料 $info = $userModel->find(1); // 刪除主表和關聯表 $del = $info->together(['userExtend'])->delete(); }