关于thinkphp关联模型的HAS_ONE

距离第一次学习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效率高,下回再分析,如有不对,希望大家指正。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值