您的位置:首页 > 编程语言 > PHP开发

关于thinkphp关联模型的HAS_ONE

2014-05-30 14:30 726 查看
距离第一次学习thinkphp有一年多了,也算是认识它和使用它一年多了吧,但是发现我对thinkphp还是有些不熟悉的地方,特别是模型。

最近开发二手房项目,里面用到模型,然后数据库是沿用之前的,框架转为thinkphp,之前一直自己设计数据库,所以模型可以做的和官方一样,但是这次因为数据库不便于改动,所以用HAS_ONE的时候,发现问题了。

我用的是3.1.3版本,

官方手册给出的HAS_ONE模型支持的关联属性是这样的:

mapping_type关联类型,这个在HAS_ONE 关联里面必须使用HAS_ONE 常量定义。
class_name要关联的模型类名

例如,class_name 定义为Profile的话则表示和另外的Profile模型类关联,这个Profile模型类是无需定义的,系统会自动定位到相关的数据表进行关联。
mapping_name关联的映射名称,用于获取数据用

该名称不要和当前模型的字段有重复,否则会导致关联数据获取的冲突。如果mapping_name没有定义的话,会取class_name的定义作为mapping_name。如果class_name也没有定义,则以数组的索引作为mapping_name。
foreign_key关联的外键名称

外键的默认规则是当前数据对象名称_id,例如:

UserModel对应的可能是表think_user (注意:think只是一个表前缀,可以随意配置)

那么think_user表的外键默认为 user_id,如果不是,就必须在定义关联的时候显式定义 foreign_key 。
condition关联条件

关联查询的时候会自动带上外键的值,如果有额外的查询条件,可以通过定义关联的condition属性。
mapping_fields关联要查询的字段

默认情况下,关联查询的关联数据是关联表的全部字段,如果只是需要查询个别字段,可以定义关联的mapping_fields属性。
as_fields直接把关联的字段值映射成数据对象中的某个字段

这个特性是ONE_TO_ONE 关联特有的,可以直接把关联数据映射到数据对象中,而不是作为一个关联数据。当关联数据的字段名和当前数据对象的字段名称有冲突时,还可以使用映射定义。
我现在需要关联两种表:
Community(小区表)和Region(地区表)

小区表里的字段region和地区表里的rid是关联的

小区表



地区表



按照官方文档,写出来的模型是这样的

<?php
class CommunityModel extends RelationModel
{
protected $_link = array(
'region'=>array(
'mapping_type'=>HAS_ONE,
'class_name'=>'region',
'mapping_name'=>'region',
'foreign_key' =>'rid',
),

);

}
然后action那边是

$id = $_GET['cid'];
$Community = D('Community');
$where['cid'] = $id;
$list = $Community->relation('region')->where($where)->select();
打印出来的结果是

array(1) {
[0] => array(27) {
["cid"] => string(18) "201201121788888888"
["username"] => string(14) "abc"
["name"] => string(18) "城市花园"
["region"] => NULL

很明显关联没成功,我又回去查看官方手册,官方是这么说道

foreign_key:关联的外键名称

外键的默认规则是当前数据对象名称_id,例如:

UserModel对应的可能是表think_user (注意:think只是一个表前缀,可以随意配置)

那么think_user表的外键默认为 user_id,如果不是,就必须在定义关联的时候显式定义 foreign_key 。

我foreign_key没定义错,是rid,但是我需要关联的是小区表里的region=地区表里的rid呀,

怎么办,这个时候开源的优势就出来了,直接查看tp的源代码,

打开thinkphp/Extend/Model/RelationModel.php

找到第130行getRelation方法

今天是研究HAS_ONE 关键是看HAS_ONE

第149-156行代码

case HAS_ONE:
$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;



关键是151行
$mappingCondition .= " AND {$mappingFk}='{$pk}'";
这里就是关联的条件了,平时我们left join table on  A=B

就是这里啦,然后再往上找$mappingFk和$pk变量

第135行-145行代码中有

$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(); // 关联键名
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($this->name).'_id';     //  关联外键
}
下面是查找$mappingFk经过

151行$mappingFk

144行

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


下面是查找$pk经过

151行$pk

150行$pk   =  $result[$mappingKey];

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

重点139行,mapping_key是否不是为空,不是空则 $val['mapping_key'],是空就$this->getPk()

$this->getPk();是获取主键的方法,我们定义模型的时候,官方文档没告诉我们定义mapping_key,所以tp默认找主键,所以我们关联的时候,如果两个表不是主键关联,那么将无法关联

下面是解决方案

<?php
class CommunityModel extends RelationModel
{
protected $_link = array(

'region'=>array(//关联的表名
'mapping_type'=>HAS_ONE,  //关联类型
'class_name'=>'region', //需要关联的模型类名
'mapping_key' =>'region', //Community中关联的字段名
'mapping_name'=>'region', //关联的映射名称,用于获取数据用
'foreign_key' =>'rid', //region中关联的字段名
),

);

}
其实就是加了
'mapping_key' =>'region', //Community中关联的字段名
这样就可以了,tp手册中没告诉我们,可能是因为怕我们使用的时候没加索引,影响效率吧,

还有,一般我是HAS_MANY的时候才用tp的关联模型,因为HAS_ONE还不如直接left join效率高,下回再分析,如有不对,希望大家指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息