關聯模型欄位取別名查詢不出資料的處理方法

zgxxx發表於2019-01-17

需求

file
查詢這樣的有父子層次關係的選單資料,例如id=2的資料有子資料,id=3,4,5,6,16,因為後面這些資料的parent_id都是2

比較笨的辦法

先查詢每一條資料,然後再遍歷查詢parent_id = 每一條資料的id,最後陣列拼接。這種方法不僅笨,查詢次數還多,完全沒必要。

模型自關聯

laravel自帶的ORM是個神器,針對這種有關係的資料,完全可以使用關係模型,既簡單又實用,由於這裡只有一個資料表,關係也存在於同一張表,所以可以直接使用自關聯,將兩個關係定義在同一個Model裡面:

    ......
    public function parent() {
        return $this->hasOne($this, 'id', 'parent_id');
    }

    public function children() {
        return $this->hasMany($this, 'parent_id', 'id');
    }

關係定義裡面的引數也可以這樣寫:

    ......
    public function parent() {
        return $this->hasOne(get_class($this), $this->getKeyName(), 'parent_id');
    }

    public function children() {
        return $this->hasMany(get_class($this), 'parent_id', $this->getKeyName());
    }

查詢包含子選單的資料

        return Admin_menu::with(['children' => function($query){
                $query->select('id', 'title', 'parent_id');
            }])
            ->select('id', 'title', 'parent_id')
            ->get();

file

同理查詢包含父選單的資料,將with引數'children'換成'parent'即可。

關聯鍵取別名查詢不出資料

        return Admin_menu::with(['children' => function($query){
                $query->select('id', 'title', 'parent_id');
            }])
            ->select('id as MainId', 'title', 'parent_id')
            ->get();

file

分析sql語句

    \DB::connection()->enableQueryLog(); // 開啟查詢日誌
    $menus =  Admin_menu::with(['children' => function($query){
        $query->select('id', 'title', 'parent_id');
    }])
    ->select('id as MainId', 'title', 'parent_id')
    ->get();
    foreach (\DB::getQueryLog() as $sql) {
        dump($sql['query']);
    }

file
列印sql語句,不取別名的情況下,第二條查詢,bindings應該是有值的陣列,也就是 where in ()是有值可以查詢的;給id取了別名後會發現,binding變成null,where in(null)也就查不到資料。

這裡我的猜想是,where in (array)這裡的array是依賴主鍵的名稱的,在關聯查詢的時候,已經定義了id = [3,4,5,6...],但是我們最後給id取了別名,變成MaindId,所以找不到名為id的陣列。
如果真是這樣,我們試著再給它加上id,讓它能夠找到名為id的陣列

    \DB::connection()->enableQueryLog(); // 開啟查詢日誌
    $menus =  Admin_menu::with(['children' => function($query){
        $query->select('id', 'title', 'parent_id');
    }])
    ->select('id', 'id as MainId', 'title', 'parent_id')
    ->get();
    foreach (\DB::getQueryLog() as $sql) {
        dump($sql['query']);
    }

file
這裡可以看到bingdings已經不再是null了。

總結

雖然以上取別名問題所在只是我的猜想,但大致可以得出結論:依賴關聯主鍵localKey的查詢,不能缺少相應的欄位,也就是說select應該包含對應localKey,如果要取別名應該額外新增,形如:

select('id', 'id as MainId', 'title', 'parent_id', 'parent_id as extraId')

附加另外一種處理資料格式方法

另外寫一個私有的格式處理方法transformer,取別名就交給這個方法來轉換

    public function test() {
        $menus = Admin_menu::with(['parent' => function($query){
                $query->select('id', 'title', 'parent_id');
            }])
            ->select('id', 'title', 'parent_id')
            ->get();
        return $this->transformer($menus);
    }

    protected function transformer($items) {
        $data = [];
        foreach ($items ?? [] as $item) {
            $data[] = [
                'mainId' => $item->id,
                'title' => $item->title,
                'parent_id' => $item->parent_id,
                'children' => $item->children,
            ];
        }
        return $data;
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章