前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >谨慎使用 Laravel 的 Model 复制(replicate)功能

谨慎使用 Laravel 的 Model 复制(replicate)功能

作者头像
overtrue
发布2020-07-24 16:35:27
2.8K0
发布2020-07-24 16:35:27
举报

今天分享一个刚挖的坑,我们的产品有一个交互是用户可以复制自己的日程,我们当时实现的时候仅仅花了不到半小时就上线了,完事还夸了 Eloquent 真的是面面俱到,连复制功能都做好了,代码如下:

代码语言:javascript
复制
$item = Item::find($request->input('copy_from'))
        ->replicate()
        ->fill(['copy_from' => $request->input('copy_from')])
        ->save();

就这样完成了一条记录的复制,是不是非常简单?

后来因为我们为了加快查询,为 json 字段中某些关键字段加了虚拟字段:

代码语言:javascript
复制
Schema::table('items', function (Blueprint $table) {
    $table->unsignedBigInteger('v_meeting_id')
            ->index()
            ->nullable()
            ->virtualAs('CASE WHEN `properties`->>"$.meeting_id" = "null" THEN 0 ELSE `properties`->>"$.meeting_id" END');
});

关于虚拟字段的内容可以参考:http://mysql.taobao.org/monthly/2017/12/09/,在 Laravel migration 中的用法如上,不过我加了一些条件处理。

就在今天线上报错了,这个复制功能报错:

代码语言:javascript
复制
General error: 3105 The value specified for generated column 'v_meeting_id' in table 'items' is not allowed.

我检查 SQL 才发现 $item->replicate() 是直接对 Model 的 $attributes 字段复制,也就是不会经过 $fillable 字段过滤,导致最终生成的 insert 语句中存在虚拟字段赋值,导致了上面的报错。

于是复制逻辑不得不修改为:

代码语言:javascript
复制
$item = Item::create(
            array_merge(
                Item::find($request->input('copy_from'))->toArray(),
                ['copy_from' => $request->input('copy_from')]
            )
        );

回头再来看看 replicate 的源码:

代码语言:javascript
复制
public function replicate(array $except = null)
    {
        $defaults = [
            $this->getKeyName(),
            $this->getCreatedAtColumn(),
            $this->getUpdatedAtColumn(),
        ];
        $attributes = Arr::except(
            $this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults
        );

        return tap(new static, function ($instance) use ($attributes) {
            $instance->setRawAttributes($attributes);

            $instance->setRelations($this->relations);

            $instance->fireModelEvent('replicating', false);
        });
    }

可以看到直接将当前实例的 $attributes 排除掉 $except 后写入新的实例,然后复制关系,并没有走 fill 方法,所以 $fillable 就没用上。

所以这是掉到了自己给自己挖的坑里。大家周末愉快!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 假装我会写代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档