讲解一下ThinkPHP的关联模型

导读thinkphp关联模型的,我一开始学习的的时候觉得对于一个逻辑业务简单,不太复杂的项目真没必要的去弄什么关联模型的。本质我想说我们再学php的时候不要的为了MVC而MVC,其次通过页面trace可以看出生成的sql语句效率不是太高的。但是如果你做一个cms或者比较复杂的系统的,系统本身表的结构就是很复杂的话,那么使用关联模型可以是整个业务逻辑变得有紧有条的,在这种的情况下我是比较推荐的。

   首先我要讲的是官方对关联的模型的解释和介绍:

   关联关系

通常我们所说的关联关系包括下面三种:
   一对一关联 :ONE_TO_ONE,包括HAS_ONE和BELONGS_TO

   一对多关联 :ONE_TO_MANY,包括HAS_MANY和BELONGS_TO

   多对多关联 :MANY_TO_MANY
   关联关系必然有一个参照表,例如:

       有一个员工档案管理系统项目,这个项目要包括下面的一些数据表:基本信息表、员工档案表、部门表、项目组表、银行卡表(用来记录员工的银行卡资料)。
       这些数据表之间存在一定的关联关系,我们以员工基本信息表为参照来分析和其他表之间的关联:
           每个员工必然有对应的员工档案资料,所以属于HAS_ONE关联;
           每个员工必须属于某个部门,所以属于BELONGS_TO关联;
           每个员工可以有多个银行卡,但是每张银行卡只可能属于一个员工,因此属于HAS_MANY关联;
           每个员工可以同时在多个项目组,每个项目组同时有多个员工,因此属于MANY_TO_MANY关联;


   分析清楚数据表之前的关联关系后,我们才可以进行关联定义和关联操作。

   。。。。。。。剩下都是通过手册可以看到的。。。。。。。。。


       好了 不都说什么的了。我讲解的是很简单的例子,假设我有两张表。一张是学生信息表tp_stus,一张是学生对应的班级表tp_classes。两张表各有一个主键,sid和cid,结构如下图:

   1.表tp_stus:

2.表tp_classes

在IndexAction中写入如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// 本类由系统自动生成,仅供测试用途

classIndexAction extendsAction {

publicfunctionindex(){

if(!empty($_POST['up'])){

$Stus=D('Stus');

$Stus->create();

$Stus=$Stus->add();

}

//$Stus=D('Stus');

if(!empty($_POST['search'])){

$Stus=D('Stus');

$arr=$Stus->relation('class')->select();

}

//dump($arr);

$this->assign('arr',$arr);

$this->display();

}

}


在StusModel中写入如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

classStusModel extendsRelationModel{

protected$_auto=array(

array('time','time',3,'function'), // 对create_time字段在更新的时候写入当前时间戳

);

protected$_link=array(

'Classes'=>array(

'mapping_type'=>BELONGS_TO,

//'class_name'=>'Classes',

//外键,也就是表Stus中的字段

'foreign_key'=>'cid',

'mapping_name'=>'class',

//关联的字段,可以多个

'mapping_fields'=>'cname',

'as_fields'=>'cname:classname'

),

);

}



模版index.html就会显示如下的结构:

生成的sql语句如下:

       tips:数据量小的话是感觉和'select * from tp_stus,tp_classes where tp_stus.cid=tp_classes.cid,等效的语句没区别。也就是说关联模型最后的生成的sql语句其实是一个连接查询。数据关联处理复杂的时候就明显体现优势了,方便很多。告诉大家一下不同关联类型生成语句也是不同的呢

   大家可以看一下ThinkPHP关联模型的RelationModel.class.php源码是怎么处理生成sql语句的?

   RelationModel.class.php第130 line 的源码显示:

1

2

3

4

5

$mappingType= !empty($val['mapping_type'])?$val['mapping_type']:$val;//关联类型

$mappingClass= !empty($val['class_name'])?$val['class_name']:$key;// 关联类名

$mappingFields= !empty($val['mapping_fields'])?$val['mapping_fields']:'*';// 映射字段

$mappingCondition= !empty($val['condition'])?$val['condition']:'1=1';// 关联条件

$mappingKey=!empty($val['mapping_key'])? $val['mapping_key'] : $this->getPk(); // 关联键名


   后面的就是开始处理不同关联类型的model语句了,源码这样处理的我已经注释好了:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

// 获取关联模型对象

$model= D($mappingClass);

switch($mappingType) {

caseHAS_ONE://一对一

//关联类型构造model查询语句

$pk=  $result[$mappingKey];

$mappingCondition.= " AND {$mappingFk}='{$pk}'";

$relationData=$model->where($mappingCondition)->field($mappingFields)->find();

if(!empty($val['relation_deep'])){

$model->getRelation($relationData,$val['relation_deep']);

}                    

break;

caseBELONGS_TO://一对多

if(strtoupper($mappingClass)==strtoupper($this->name)) {

// 自引用关联 获取父键名

$mappingFk=!empty($val['parent_key'])? $val['parent_key'] : 'parent_id';

}else{

$mappingFk=   !empty($val['foreign_key'])?$val['foreign_key']:strtolower($model->getModelName()).'_id';     //  关联外键

}

$fk=  $result[$mappingFk];

$mappingCondition.= " AND {$model->getPk()}='{$fk}'";

//关联类型构造model查询语句

$relationData=  $model->where($mappingCondition)->field($mappingFields)->find();

if(!empty($val['relation_deep'])){

$model->getRelation($relationData,$val['relation_deep']);

}                    

break;

caseHAS_MANY://一对多

$pk=  $result[$mappingKey];

$mappingCondition.= " AND {$mappingFk}='{$pk}'";

$mappingOrder=  !empty($val['mapping_order'])?$val['mapping_order']:'';

$mappingLimit=  !empty($val['mapping_limit'])?$val['mapping_limit']:'';

// 延时获取关联记录  注意这边开始生成HAS_MANY关联类型构造model查询语句

$relationData=  $model->where($mappingCondition)->field($mappingFields)->order($mappingOrder)->limit($mappingLimit)->select();

if(!empty($val['relation_deep'])){

foreach($relationDataas$key=>$data){                        

$model->getRelation($data,$val['relation_deep']);

$relationData[$key]     =   $data;

}                          

}

break;

caseMANY_TO_MANY://多对多

$pk=  $result[$mappingKey];

$mappingCondition" {$mappingFk}='{$pk}'";

$mappingOrder=  $val['mapping_order'];

$mappingLimit=  $val['mapping_limit'];

$mappingRelationFk$val['relation_foreign_key']?$val['relation_foreign_key']:$model->getModelName().'_id';

$mappingRelationTable=  $val['relation_table']?$val['relation_table']:$this->getRelationTableName($model);

//关联类型构造model查询语句

$sql"SELECT b.{$mappingFields} FROM {$mappingRelationTable} AS a, ".$model->getTableName()." AS b WHERE a.{$mappingRelationFk} = b.{$model->getPk()} AND a.{$mappingCondition}";

if(!empty($val['condition'])) {

$sql.= ' AND '.$val['condition'];

}

if(!empty($mappingOrder)) {

$sql.= ' ORDER BY '.$mappingOrder;

}

if(!empty($mappingLimit)) {

$sql.= ' LIMIT '.$mappingLimit;

}

$relationData=   $this->query($sql);

if(!empty($val['relation_deep'])){

foreach($relationDataas$key=>$data){                        

$model->getRelation($data,$val['relation_deep']);

$relationData[$key]     =   $data;

}                          

}                    

break;

}


   大家看了源码可能还不是很一下子理解,下面总结一下,便于对比理解:

   强调一下Thinkphp中表关联HAS_ONE和BELONGS_TO的区别:

   HAS_ONE

       例子:
       父关联对象表:
           cid
           cname
       子关联对象表:
           sid
           sname
           cid
   外键是:c_id

   BELONGS_TO:外键在你父联对象中
   父关联对象表:
       cid
       class_id
       product_name
   子关联对象表:
       class_id
       class_name
   外键是:
class_id

最后要说的也就是关联的相关操作了,无非就是增删改查一些操作。这一些都是建立在你定义关联基础上操作和之前crud没啥区别的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值