ThinkPHP一對一關聯模型的運用(ORM)

疯子丶pony發表於2024-07-25

一、序言

最近在寫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(); }

相關文章