1、業務需求
業務系統中現有:學生表、班級表、學校表。
現需要查詢學生表的同時關聯班級表和學校表以查詢某個學生屬於哪個班級和哪個學校。
2、三表結構
學生表 student
(只列出部分字段):
字段名 | 含義 |
---|---|
id | 主鍵 |
room_id | 關聯班級表外鍵 |
name | 學生姓名 |
sex | 學生性別 |
address | 家庭住址 |
班級表 room
:
字段名 | 含義 |
---|---|
id | 主鍵 |
school_id | 關聯學校表外鍵 |
name | 班級名稱 |
學校表 school
:
字段名 | 含義 |
---|---|
id | 主鍵 |
name | 學校名稱 |
3、三表關係
4、主從關係
先來看一下主從表的定義:
主表: 被作爲外鍵引用的表。
從表: 有外鍵引用的表。
也就是說,在兩個表的關係中,有外鍵的表叫做從表,只有主鍵而沒有外鍵的表叫做主表。
那麼,學生表和班級表的關係:學生表是從表(存在外鍵room_id),班級表是主表。
同樣,班級表和學校表的關係:班級表是從表(存在外鍵school_id),學校表是主表。
6、模型一對一關聯的用法
在 ThinkPHP5
中給出了兩種一對一關聯的方法:
hasOne
和 belongsTo
那麼,什麼時候用hasOne
什麼時候用 belongsTo
呢?
其實很簡單,通過表的主從關係就可以得出(因爲表和模型是相對應的):
1、在從表中建立關聯關係用belongsTo
,可以理解爲從表有從屬於(主表)的意思。
2、在主表中建立關聯關係用hasOne
,可以理解爲從主表去對應從表的數據時只會對應單個。
7、代碼實現
理清了以上的關係之後,我們就可以愉快的寫代碼了,先來看學生模型中(StudentModel
)的關聯方法定義:
public function room()
{
return $this->belongsTo('RoomModel', 'room_id', 'id');
}
很簡單,學生從屬於班級,用belongsTo
方法,第一個參數爲要關聯的模型名(這裏去關聯班級模型),第二個參數爲當前模型(StudentModel
)或者說表(student
)的關聯外鍵(room_id
),第三個參數爲被關聯模型(RoomModel
)或者說表(room
)的主鍵(id
)。
這樣學生關聯班級就完成了,接下來再來看班級關聯學校(RoomModel
)中的關聯方法定義:
public function school()
{
return $this->belongsTo('SchoolModel', 'school_id', 'id');
}
也很簡單,班級從屬於學校,同樣用belongsTo
方法。
至此,關聯方法定義好了,那怎麼從控制器中去調用查詢呢,請往下看:
我們在學生(Student
)控制器中去調用查詢(因爲我們主要的目的就是查詢學生表):
$data = StudentModel::with(['room' => ['school']])->select()->toArray();
首先 with()
方法接受一個數組,可以理解爲當前模型(StudentModel
)先去關聯班級模型(RoomModel
),然後班級模型又去關聯學校模型(SchoolModel
)。當然關聯查詢時寫的是在相應模型中定義的關聯方法(room
和school
),而不是模型名。
我們看打印查詢出來的數據結構(只列出一條):
array(5) {
[0] => array(10) {
["id"] => int(1)
["image"] => string(6) "暫無"
["name"] => string(9) "張三"
["sex"] => string(3) "男"
["idcard"] => string(18) "37*********1X"
["address"] => string(27) "山東省濟南市"
["room_id"] => int(5)
["status"] => string(3) "否"
["create_time"] => string(19) "2020-04-14 11:56:04"
["room"] => array(4) {
["id"] => int(5)
["name"] => string(10) "Java一班"
["school_id"] => int(1)
["school"] => array(2) {
["id"] => int(1)
["name"] => string(37) "山東大學"
}
}
}
}
可以看到在學生數據的數組裏面多了一個room
元素,而在班級數據的數組裏面又多了一個school
元素,這就是模型關聯後查詢出來的數據結構。
如果想在模板文件中循環輸出可以這麼寫:
<td>{$vo.name}</td>
<td>{$vo.sex}</td>
<td>{$vo.idcard}</td>
<td>{$vo.address}</td>
<!-- 顯示學校名稱 -->
<td>{$vo.room.school.name}</td>
<!-- 顯示班級名稱 -->
<td>{$vo.room.name}</td>
如果想把班級的數據和學校的數據並列到學生數組怎麼辦呢?
我們可以用bind()
方法將子模型的屬性綁定到父模型下,那麼修改模型中關聯方法如下:
public function school()
{
return $this->belongsTo('SchoolModel', 'school_id', 'id')
->bind(['school_name2' => 'name']);
}
public function room()
{
return $this->belongsTo('RoomModel', 'room_id', 'id')
->bind(['room_name' => 'name', 'school_name' => 'school_name2']);
}
解釋一下:
因爲我們三個表中都有name
字段,所以在綁定過去的時候,爲了不和學生表中的name
重複,我們需要換一個名字(起別名)再綁定過去。
因爲是綁定到父模型,所以我們首先要把SchoolModel
中的屬性綁定到RoomModel
,再把RoomModel
中的屬性綁定到StudentModel
,層層往前遞進。
1、首先看school()
方法裏面,我們把學校名稱name
字段起別名爲school_name2
(注意數組裏面寫法是由後往前,鍵是新字段名,值是舊字段名),然後綁定到其父模型(RoomModel
)。
2、再看room()
方法裏面,我們不僅需要把班級名稱name
字段重命名綁定到StudentModel
,還要把其子模型綁定過來的school_name2
字段也重命名綁定到StudentModel
。
如此,綁定也就完成了,再來看下打印出的數據結構(只列出一條):
array(5) {
[0] => array(11) {
["id"] => int(1)
["image"] => string(6) "暫無"
["name"] => string(9) "張三"
["sex"] => string(3) "男"
["idcard"] => string(18) "37*********1X"
["address"] => string(27) "山東省濟南市"
["room_id"] => int(5)
["status"] => string(3) "否"
["create_time"] => string(19) "2020-04-14 11:56:04"
// 這是綁定過來的字段
["room_name"] => string(10) "Java一班"
["school_name"] => string(37) "山東大學"
}
}
同樣的道理,在模板文件循環輸出時就可以這樣寫了:
<td>{$vo.school_name}</td>
<td>{$vo.room_name}</td>
本文結束。