在Thinkphp中,关联模型经常会被使用到,然而最近在使用的时候发现关联模型中的自动生成功能有些问题。
例如有一个类IdeaDetailModel对应表oidea_idea_detail,它有一个简表oidea_idea_simple(类IdeaSimpleModel),IdeaDetailModel代码如下(只保留了部分功能):
<?php
namespace Idea\Model;
use Think\Model\RelationModel;
class IdeaDetailModel extends RelationModel
{
protected $tableName = 'idea_detail';
/* 关系模型 */
protected $_link = array(
// 与IdeaSimple关联(一对一:HAS_ONE)
'Simple' => array(
'mapping_type' => self::HAS_ONE,
'class_name' => 'IdeaSimple',
'mapping_name' => 'Simple',
'foreign_key' => 'iid',
'relation_foreign_key' => 'id',
'mapping_fields' => 'star,heart,watch',
),
);
/* Idea模型自动完成 */
protected $_auto = array(
array('read', 0, self::MODEL_INSERT),
array('comment', 0, self::MODEL_INSERT),
array('status', 1, self::MODEL_INSERT),
array('create_time', NOW_TIME, self::MODEL_INSERT),
array('update_time', NOW_TIME, self::MODEL_BOTH),
);
public function addIdea($uid, $arrIdeaInfo = null)
{
/*做些验证*/
//写入数据库
$data = array('src_uid' => $uid, 'title' => $arrIdeaInfo['title'], 'summary' => $arrIdeaInfo['summary']);
$data['Simple'] = $data;
$data['content'] = $arrIdeaInfo['content'];
$data = $this->create($data);
if (!$data) {
return false;
}
$res = $this->relation(array('Simple'))->add($data);
return $res;
}
}
IdeaSimpleModel类代码如下(只保留部分功能):
<?php
namespace Idea\Model;
use Think\Model\RelationModel;
/**
*
*/
class IdeaSimpleModel extends RelationModel
{
/* Idea模型自动完成 */
protected $_auto = array(
array('star', 0, self::MODEL_INSERT),
array('heart', 0, self::MODEL_INSERT),
array('watch', 0, self::MODEL_INSERT),
array('status', 1, self::MODEL_INSERT),
array('update_time', NOW_TIME, self::MODEL_BOTH),
);
}
如果我们调用IdeaDetailModel类的addIdea方法,实际上是不能实现IdeaSimpleModel中的自动完成功能的。
解决方法如下(参考了http://www.thinkphp.cn/code/833.html,实际上是对Think\RelationModel类的部分函数进行了覆盖,而我在其中添加了函数createRelationData):
<?php
namespace Common\Model;
use Think\Model\RelationModel;
/**
* 关联模型自动完成类,重载RelationModel中的create方法
* 在使用关联模型并且需要自动完成的时候使用该类
*/
class AutoCompleteRelationModel extends RelationModel
{
/**
* 重载create方法,不过滤字段,并且生成需要数据
*/
function create($data = '', $type = '') {
// 如果没有传值默认取POST数据
if (empty($data)) {
$data = $_POST;
} elseif (is_object($data)) {
$data = get_object_vars($data);
}
// 验证数据
if (empty($data) || !is_array($data)) {
$this->error = L('_DATA_TYPE_INVALID_');
return false;
}
// 状态
$type = $type ? $type : (!empty($data[$this->getPk() ]) ? self::MODEL_UPDATE : self::MODEL_INSERT);
// 数据自动验证
if (!$this->autoValidation($data, $type)) return false;
// 表单令牌验证
if (!$this->autoCheckToken($data)) {
$this->error = L('_TOKEN_ERROR_');
return false;
}
// 验证完成生成数据对象
if ($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
$fields = $this->getDbFields();
foreach ($data as $key => $val) {
if (MAGIC_QUOTES_GPC && is_string($val)) {
$data[$key] = stripslashes($val);
}
}
}
// 创建完成对数据进行自动处理
$data=$this->autoOperation($data, $type);
$data = $this->createRelationData($data);
// $data=$this->createData($data);
// 返回创建的数据以供其他调用
return $data;
}
/**
* 自动表单处理
* @access public
* @param array $data 创建数据
* @param string $type 创建类型
* @return mixed
*/
private function autoOperation($data, $type) {
if (!empty($this->options['auto'])) {
$_auto = $this->options['auto'];
unset($this->options['auto']);
} elseif (!empty($this->_auto)) {
$_auto = $this->_auto;
}
// 自动填充
if (isset($_auto)) {
foreach ($_auto as $auto) {
// 填充因子定义格式
// array('field','填充内容','填充条件','附加规则',[额外参数])
if (empty($auto[2])) $auto[2] = self::MODEL_INSERT; // 默认为新增的时候自动填充
if ($type == $auto[2] || $auto[2] == self::MODEL_BOTH) {
switch (trim($auto[3])) {
case 'function': // 使用函数进行填充 字段的值作为参数
case 'callback': // 使用回调方法
$args = isset($auto[4]) ? (array)$auto[4] : array();
if (isset($data[$auto[0]])) {
array_unshift($args, $data[$auto[0]]);
}
if ('function' == $auto[3]) {
$data[$auto[0]] = call_user_func_array($auto[1], $args);
} else {
$data[$auto[0]] = call_user_func_array(array(&$this,
$auto[1]
) , $args);
}
break;
case 'field': // 用其它字段的值进行填充
$data[$auto[0]] = $data[$auto[1]];
break;
case 'ignore': // 为空忽略
if ('' === $data[$auto[0]]) unset($data[$auto[0]]);
break;
case 'string':
default: // 默认作为字符串填充
$data[$auto[0]] = $auto[1];
}
if (false === $data[$auto[0]]) unset($data[$auto[0]]);
}
}
}
return $data;
}
/**
* 生成关联模型需要的数据
*/
function createData($data) {
foreach ($data as $k => $v) {
if (in_array($k, $this->fields)) {
$data['Detail'][$k] = $v;
unset($data[$k]);
}
}
//删除多余字段
unset($data['nid']);
unset($data['create_date']);
return $data;
}
/**
* ADD
* 对关联模型使用D函数,从而实现自动完成
*/
function createRelationData($data)
{
$linkKeys = array_keys($this->_link);
if (empty($linkKeys) || !is_array($linkKeys)) {
return $data;
}
$arrMappingName = array();
foreach ($linkKeys as $index => $key) {
if (isset($this->_link[$key]['mapping_name'])) {
array_push($arrMappingName, $this->_link[$key]['mapping_name']);
unset($linkKeys[$index]);
}
}
$arrMappingName = array_merge($arrMappingName, $linkKeys);
foreach ($data as $model => $item) {
if (in_array($model, $arrMappingName)) {
$tempData = D($this->_link[$model]['class_name'])->create($item);
$data[$model] = $tempData;
}
}
return $data;
}
}
这样就可以让关联模型支持自动完成功能了。使用方法:
<?php
namespace Idea\Model;
use Common\Model\AutoCompleteRelationModel;
// use Think\Model\RelationModel;
/**
* Idea model
* 对应表oidea_idea
*/
class IdeaDetailModel extends AutoCompleteRelationModel
{
/*同上,不变*/
}
水平有限,欢迎指正~