You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10中JSON字段写入Carbon时间出现无效值问题排查

问题

在Laravel 10项目中,需要将登录用户信息及登录时间写入JSON字段。代码中使用Carbon::now($timezone)获取时间,日志显示时区(Europe/Kiev)和时间均正常,但数据库data字段中的online_from值却无效。相关代码、日志输出、表迁移及建表语句如下:

业务代码

\Log::info(varDump($timezone, ' -1 $timezone::'));
\Log::info(varDump(Carbon::now($timezone), ' -1 BEFORE EXIT Carbon::now($timezone)::'));
return KeyData::create([
    'key' => $key,
    'data' => [["user_id" => $loggedUserId, "online_from" => Carbon::now($timezone), 'temp_field' => 'temp_value']],
    'expired_at' => Carbon::now($timezone)->addSeconds($cachingTimeInSeconds)
]);

日志输出

-1 $timezone:: : Europe/Kiev
[2024-05-23 10:03:10] local.INFO:  (Object of Carbon\Carbon) : -1 BEFORE EXIT Carbon::now($timezone):: : Array
(
    [ * endOfTime] =>
    [ * startOfTime] =>
    [ * constructedObjectId] => 000000000000055b0000000000000000
    [ * localMonthsOverflow] =>
    [ * localYearsOverflow] =>
    [ * localStrictModeEnabled] =>
    [ * localHumanDiffOptions] =>
    [ * localToStringFormat] =>
    [ * localSerializer] =>
    [ * localMacros] =>
    [ * localGenericMacros] =>
    [ * localFormatFunction] =>
    [ * localTranslator] =>
    [ * dumpProperties] => Array
        (
            [0] => date
            [1] => timezone_type
            [2] => timezone
        )

    [ * dumpLocale] =>
    [ * dumpDateProperties] =>
    [date] => 2024-05-23 10:03:10.253696
    [timezone_type] => 3
    [timezone] => Europe/Kiev
)

表迁移代码

Schema::create('key_data', function (Blueprint $table) {
    $table->id();
    $table->string('key', 255);
    $table->json('data')->nullable();
    $table->dateTime('expired_at');
    $table->timestamp('created_at')->useCurrent();

    $table->index(['key', 'expired_at'], 'key_data_key_expired_at_index');
});

建表语句

CREATE TABLE `key_data` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`data` json DEFAULT NULL,
`expired_at` datetime NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `key_data_key_expired_at_index` (`key`,`expired_at`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

原因分析

Laravel处理JSON字段时,会自动将数组序列化为JSON字符串,但Carbon对象无法直接被序列化为有效的JSON时间格式。直接将Carbon实例放入data数组中时,Laravel会把它转为包含内部属性的数组(如日志显示的Carbon内部结构),JSON序列化后会保留这些无用的内部字段,导致online_from存储值无效。

而expired_at字段正常,是因为它被定义为dateTime类型,Laravel ORM会针对日期字段自动将Carbon对象转换为数据库可识别的datetime格式,但JSON字段没有这种自动转换逻辑。

修复方案

需将Carbon对象显式转换为字符串格式,推荐使用标准格式确保JSON序列化后是有效的时间字符串。

方案1:使用ISO 8601标准格式(推荐)

return KeyData::create([
    'key' => $key,
    'data' => [["user_id" => $loggedUserId, "online_from" => Carbon::now($timezone)->toIso8601String(), 'temp_field' => 'temp_value']],
    'expired_at' => Carbon::now($timezone)->addSeconds($cachingTimeInSeconds)
]);

方案2:自定义时间格式

如果需要特定格式(如Y-m-d H:i:s),使用format()方法:

return KeyData::create([
    'key' => $key,
    'data' => [["user_id" => $loggedUserId, "online_from" => Carbon::now($timezone)->format('Y-m-d H:i:s'), 'temp_field' => 'temp_value']],
    'expired_at' => Carbon::now($timezone)->addSeconds($cachingTimeInSeconds)
]);

方案3:模型中定义自动转换(更优雅)

在KeyData模型中,通过Attribute自定义data字段的转换逻辑,自动处理Carbon对象:

class KeyData extends Model
{
    protected function data(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => json_decode($value, true),
            set: function ($value) {
                if (isset($value[0]['online_from']) && $value[0]['online_from'] instanceof Carbon) {
                    $value[0]['online_from'] = $value[0]['online_from']->toIso8601String();
                }
                return json_encode($value);
            }
        );
    }
}

后续写入data字段时无需手动转换Carbon对象,模型会自动处理。

内容的提问来源于stack exchange,提问作者mstdmstd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:02:17