需求
查詢這樣的有父子層次關係的選單資料,例如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();
同理查詢包含父選單的資料,將with引數'children'換成'parent'即可。
關聯鍵取別名查詢不出資料
return Admin_menu::with(['children' => function($query){
$query->select('id', 'title', 'parent_id');
}])
->select('id as MainId', 'title', 'parent_id')
->get();
分析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']);
}
列印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']);
}
這裡可以看到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 協議》,轉載必須註明作者和本文連結