BUG报告 TP5.1.19 多对多关联写入数据报错

浏览:1230 发布日期:2018/07/20 分类:ThinkPHP5专区
重新编辑:

问题描述:
1. 多对多关联关系,a, b 表多对多, a_b 表是中间表。它们的模型分别是 AModel 和 BModel
2. A = AModel::create($data); // 成功创建 A 模型对象
3. A->b()->save($bData); // 这里报错:缺少关联表数据

通过跟代码,发现报错点是:BelongsToMany.php 文件中 attach 方法的 $id = $model->getLastInsID(); 表达式结果为0,即 getLastInsID() 返回0 导致下面执行 throw new Exception('miss relation data');

再分析代码,发现原因是 attach 方法的 $model->save($data); 方法使用了事务,即
/library/think/Model.php 文件中的 insertData 方法的代码中: $db->startTrans();

        try {
            $result = $db->strict(false)
                ->field($allowFields)
                ->insert($this->data, $this->replace, false, $sequence);

            // 获取自动增长主键
            if ($result && $insertId = $db->getLastInsID($sequence)) {
                $pk = $this->getPk();

                foreach ((array) $pk as $key) {
                    if (!isset($this->data[$key]) || '' == $this->data[$key]) {
                        $this->data[$key] = $insertId;
                    }
                }
            }

            // 关联写入
            if (!empty($this->relationWrite)) {
                $this->autoRelationInsert();
            }

            $db->commit();
使用了事务,而在事务之外调用 pdo 的 lastInsertId 方法返回0。需要在 commit 之前调用才能得到插入的ID。

Remember, if you use a transaction you should use lastInsertId BEFORE you commit
otherwise it will return 0

所以建议修改代码:
$model = new $this->model;
$model->save($data);
$id = $model->getLastInsID();

其中 $model 对象中已经保存自增主键的 id,那么 $id 从对象中获得就可以,不要使用 getLastInsID()



///////////////// 以下为旧内容////////////////////////////////

描述:a表,b表多对多关联,a_b 是中间表。当使用 $aModel->b()->attach($data) 向 b 和 a_b 表写数据时,报错:缺少关联表数据。

经过跟代码,发现 BelongsToMany.php 文件中的方法 attach 有问题。
代码如下:    public function attach($data, $pivot = [])
    {
        if (is_array($data)) {
            if (key($data) === 0) {
                $id = $data;
            } else {
                // 保存关联表数据
                $model = new $this->model;
                $model->save($data);
                $id = $model->getLastInsID();
            }
        } elseif (is_numeric($data) || is_string($data)) {
            // 根据关联表主键直接写入中间表
            $id = $data;
        } elseif ($data instanceof Model) {
            // 根据关联表主键直接写入中间表
            $relationFk = $data->getPk();
            $id         = $data->$relationFk;
        }

        if ($id) {
            // 保存中间表数据
            $pk                     = $this->parent->getPk();
            $pivot[$this->localKey] = $this->parent->$pk;
            $ids                    = (array) $id;

            foreach ($ids as $id) {
                $pivot[$this->foreignKey] = $id;
                $this->pivot->insert($pivot, true);
                $result[] = $this->newPivot($pivot, true);
            }

            if (count($result) == 1) {
                // 返回中间表模型对象
                $result = $result[0];
            }

            return $result;
        } else {
            throw new Exception('miss relation data');
        }
    }
其中问题出在:当 $data 是一个数组,即向关联表写数据。代码会执行下面中的 else 部份if (is_array($data)) {
            if (key($data) === 0) {
                $id = $data;
            } else {
                // 保存关联表数据
                $model = new $this->model;
                $model->save($data);
                $id = $model->getLastInsID();
            }
$model = new $this->model;
$model->save($data);
上面两段代码完成向关联表写数据,事实上测试用的关联表中被写了数据。
但接下来: $id = $model->getLastInsID(); 返回值为 0 !问题所在!!!
意思是得不到关联表刚插入数据的 id 字段的值。

它导致接下去执行了代码:
if ($id){}
else {throw new Exception('miss relation data');}

也就是我遇到的 缺少关联表数据 的问题!
最佳答案
评论( 相关
后面还有条评论,点击查看>>