Varobj

2020-10-20

phalcon 数据库模型报错的问题



PhalconModel 连续两次 save 操作可能会报 SQL 错误。

问题复现

当我想用一个模型实例保存新纪录之后,再次用同一个模型更新数据库部分字段时,有的时候会报错。复现如下:

// biz code
$system = new SystemConfig();
$system->key = 'foo';
$system->value = 'bar';
// 第一次保存 key=foo & vlaue=bar 的记录
$system->save();

// 发送消息队列等业务逻辑(忽略),然后获取消息id
$msg_id = 1;

$system->msg_id = msg_id;
// 第二次保存(更新msg_id到先前添加到记录上)
$system->save();

但是这里报错了,报错信息为 「Invalid datetime format: 1292 Incorrect datetime value: 'CURRENT_TIMESTAMP' for column 'created_dt' ..」, 表结构如下:

drop table if exists system_config;
create table system_config
(
    `id`         int unsigned auto_increment primary key,
    `key`        varchar(32)  default ''                not null,
    `value`      int unsigned default 0                 not null,
    `created_dt` datetime     default CURRENT_TIMESTAMP not null,
    `updated_dt` datetime     default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP
);

我的建表习惯是,必须加上创建时间和更新时间两个字段,而且使用 CURRENT_TIMESTAMP 作为默认值,如果业务代码没有指定,也能够准确记录到当前记录行的插入时间和最后更新时间。 Phalcon 框架的数据库模型会字段通过 DESCRIBE system_config 语句获取表的 DDL 等信息,然而第一次保存之后,模型实例的 created_dtupdated_dt 成员变量值居然变成 CURRENT_TIMESTAMPCURRENT_TIMESTAMP on update CURRENT_TIMESTAMP 了,再次 save 实际就是更新操作, created_dtdatetime 类型,字符串 CURRENT_TIMESTAMP 当然会报错,这个可能是 PhalconBug

临时解决

通过 BaseModelbeforeSave 方法,重写 created_dt 等字段

<?php
namespace NextJob\Model;

use Phalcon\Mvc\Model;

class BaseModel extends Model
{
    public function beforeSave(): void
    {
        /**
         * 解决:sql CURRENT_TIMESTAMP 的表
         * 第一次 save() 后,再次更新调用 save(), created_dt 值变成 "CURRENT_TIMESTAMP"
         * updated_dt 值变成 "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" 无法保存的问题
         *
         * 报错内容:
         * 「Invalid datetime format: 1292 Incorrect datetime value: 'CURRENT_TIMESTAMP' for column 'created_dt' at row 1」
         */
        if (property_exists($this, 'created_dt') && $this->created_dt === 'CURRENT_TIMESTAMP') {
            $this->created_dt = date('Y-m-d H:i:s');
        }
        if (property_exists($this, 'updated_dt') &&
            $this->updated_dt === 'CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP') {
            $this->updated_dt = date('Y-m-d H:i:s');
        }
    }
}