博主信息
布丁
博文
5
粉丝
0
评论
0
访问量
359
积分:0
P豆:12

ThinkPHP5 之对模型关联的理解

2020年09月14日 16:12:41阅读数:54博客 / 布丁/ ThinkPHP V5

1、业务需求

业务系统中现有:学生表、班级表、学校表。
现需要查询学生表的同时关联班级表和学校表以查询某个学生属于哪个班级和哪个学校。

2、三表结构

学生表 student(只列出部分字段):

字段名 含义
id 主键
room_id 关联班级表外键
name 学生姓名
sex 学生性别
address 家庭住址

班级表 room

字段名 含义
id 主键
school_id 关联学校表外键
name 班级名称

学校表 school

字段名 含义
id 主键
name 学校名称

3、三表关系

  1. graph LR
  2. A(学生表) -- 关联 --> B(班级表)
  3. B(班级表) -- 关联 --> C(学校表)
  4. A(学生表)-. 无直接关联关系 .->C(学校表)

4、主从关系

先来看一下主从表的定义:

主表: 被作为外键引用的表。
从表: 有外键引用的表。

也就是说,在两个表的关系中,有外键的表叫做从表,只有主键而没有外键的表叫做主表。

那么,学生表和班级表的关系:学生表是从表(存在外键room_id),班级表是主表。
同样,班级表和学校表的关系:班级表是从表(存在外键school_id),学校表是主表。

5、模型一对一关联的用法

ThinkPHP5 中给出了两种一对一关联的方法:
hasOnebelongsTo
那么,什么时候用hasOne 什么时候用 belongsTo呢?
其实很简单,通过表的主从关系就可以得出(因为表和模型是相对应的):

1、在从表中建立关联关系用belongsTo,可以理解为从表有从属于(主表)的意思。
2、在主表中建立关联关系用hasOne,可以理解为从主表去对应从表的数据时只会对应单个。

6、代码实现

理清了以上的关系之后,我们就可以愉快的写代码了,先来看学生模型中(StudentModel)的关联方法定义:

  1. public function room()
  2. {
  3. return $this->belongsTo('RoomModel', 'room_id', 'id');
  4. }

很简单,学生从属于班级,用belongsTo方法,第一个参数为要关联的模型名(这里去关联班级模型),第二个参数为当前模型(StudentModel)或者说表(student)的关联外键(room_id),第三个参数为被关联模型(RoomModel)或者说表(room)的主键(id)。

这样学生关联班级就完成了,接下来再来看班级关联学校(RoomModel)中的关联方法定义:

  1. public function school()
  2. {
  3. return $this->belongsTo('SchoolModel', 'school_id', 'id');
  4. }

也很简单,班级从属于学校,同样用belongsTo方法。

至此,关联方法定义好了,那怎么从控制器中去调用查询呢,请往下看:

我们在学生(Student)控制器中去调用查询(因为我们主要的目的就是查询学生表):

  1. $data = StudentModel::with(['room' => ['school']])->select()->toArray();

首先 with() 方法接受一个数组,可以理解为当前模型(StudentModel)先去关联班级模型(RoomModel),然后班级模型又去关联学校模型(SchoolModel)。当然关联查询时写的是在相应模型中定义的关联方法(roomschool),而不是模型名。

我们看打印查询出来的数据结构(只列出一条):

  1. array(5) {
  2. [0] => array(10) {
  3. ["id"] => int(1)
  4. ["image"] => string(6) "暂无"
  5. ["name"] => string(9) "张三"
  6. ["sex"] => string(3) "男"
  7. ["idcard"] => string(18) "37*********1X"
  8. ["address"] => string(27) "山东省济南市"
  9. ["room_id"] => int(5)
  10. ["status"] => string(3) "否"
  11. ["create_time"] => string(19) "2020-04-14 11:56:04"
  12. ["room"] => array(4) {
  13. ["id"] => int(5)
  14. ["name"] => string(10) "Java一班"
  15. ["school_id"] => int(1)
  16. ["school"] => array(2) {
  17. ["id"] => int(1)
  18. ["name"] => string(37) "山东大学"
  19. }
  20. }
  21. }
  22. }

可以看到在学生数据的数组里面多了一个room元素,而在班级数据的数组里面又多了一个school元素,这就是模型关联后查询出来的数据结构。

如果想在模板文件中循环输出可以这么写:

  1. <td>{$vo.name}</td>
  2. <td>{$vo.sex}</td>
  3. <td>{$vo.idcard}</td>
  4. <td>{$vo.address}</td>
  5. <!-- 显示学校名称 -->
  6. <td>{$vo.room.school.name}</td>
  7. <!-- 显示班级名称 -->
  8. <td>{$vo.room.name}</td>

如果想把班级的数据和学校的数据并列到学生数组怎么办呢?
我们可以用bind()方法将子模型的属性绑定到父模型下,那么修改模型中关联方法如下:

  1. public function school()
  2. {
  3. return $this->belongsTo('SchoolModel', 'school_id', 'id')
  4. ->bind(['school_name2' => 'name']);
  5. }
  1. public function room()
  2. {
  3. return $this->belongsTo('RoomModel', 'room_id', 'id')
  4. ->bind(['room_name' => 'name', 'school_name' => 'school_name2']);
  5. }

解释一下:
因为我们三个表中都有name字段,所以在绑定过去的时候,为了不和学生表中的name重复,我们需要换一个名字(起别名)再绑定过去。

因为是绑定到父模型,所以我们首先要把SchoolModel中的属性绑定到RoomModel,再把RoomModel中的属性绑定到StudentModel,层层往前递进。

1、首先看school()方法里面,我们把学校名称name字段起别名为school_name2(注意数组里面写法是由后往前,键是新字段名,值是旧字段名),然后绑定到其父模型(RoomModel)。
2、再看room()方法里面,我们不仅需要把班级名称name字段重命名绑定到StudentModel,还要把其子模型绑定过来的school_name2字段也重命名绑定到StudentModel

如此,绑定也就完成了,再来看下打印出的数据结构(只列出一条):

  1. array(5) {
  2. [0] => array(11) {
  3. ["id"] => int(1)
  4. ["image"] => string(6) "暂无"
  5. ["name"] => string(9) "张三"
  6. ["sex"] => string(3) "男"
  7. ["idcard"] => string(18) "37*********1X"
  8. ["address"] => string(27) "山东省济南市"
  9. ["room_id"] => int(5)
  10. ["status"] => string(3) "否"
  11. ["create_time"] => string(19) "2020-04-14 11:56:04"
  12. // 这是绑定过来的字段
  13. ["room_name"] => string(10) "Java一班"
  14. ["school_name"] => string(37) "山东大学"
  15. }
  16. }

同样的道理,在模板文件循环输出时就可以这样写了:

  1. <td>{$vo.school_name}</td>
  2. <td>{$vo.room_name}</td>

本文结束。

全部评论

文明上网理性发言,请遵守新闻评论服务协议

条评论
  • 数据库三种数据分别是:1、层次,它数据结构是一颗有向树;2、网状,它以网状结构表示实体与实体系;3、,它是以系数学论为基础
    系映射(ORM)使得处数据惊人地简单。由于以面向方式定义数据系使得查询数据变得容易,开发者不太需要注数据底层调用。
    通过操作把数据表象化,决了大部分常用场景,封装操作比起常规数据库表操作更加智能和高效,并且直观。
    系数据库是建立在基础上数据库,借助于集合代数等数学概念和方法来处数据库中数据,现实世界中各种实体以及实体各种系均用来表示。
    Vue路由是指根据url分配到程序;作用就是析URL,调用控制器(方法,并传递参数)。