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

Laravel Eloquent如何更新含动态键的JSON嵌套字段值

实现方案

Laravel Eloquent 原生不支持在JSON更新路径中使用*通配符匹配动态键,底层MySQL的JSON值修改函数要求传入明确的属性路径,你写的占位符逻辑无法直接执行,可根据你的MySQL版本选择对应实现方式:

方案1:查询后遍历匹配键名更新(兼容MySQL 5.7+,最稳定)

该方案逻辑清晰,不会出现批量误更新问题,是生产环境首选:

  1. 首先在Appointment模型中配置字段类型转换,方便直接读取JSON内容:
// app/Models/Appointment.php
protected $casts = [
    'time_slots' => 'array',
];
  1. 核心查询更新逻辑:
// 入参
$doctorId = 1;
$dayId = 1;
$targetStart = '09:00';
$targetEnd = '09:30';

// 第一步:查询存在对应时段的预约记录
$appointment = Appointment::where('doctor_id', $doctorId)
    ->where('day_id', $dayId)
    ->whereRaw('JSON_SEARCH(time_slots, "one", ?, null, "$.*.start") IS NOT NULL', [$targetStart])
    ->whereRaw('JSON_SEARCH(time_slots, "one", ?, null, "$.*.end") IS NOT NULL', [$targetEnd])
    ->first();

if (!$appointment) {
    return response()->json(['msg' => '无匹配可预约时段'], 404);
}

// 第二步:遍历动态时段,找到匹配起止时间的键名
$matchKey = null;
foreach ($appointment->time_slots as $key => $slot) {
    if ($slot['start'] === $targetStart && $slot['end'] === $targetEnd) {
        $matchKey = $key;
        break;
    }
}

// 第三步:明确路径后更新selected状态
if ($matchKey) {
    Appointment::where('id', $appointment->id)
        ->update(["time_slots->{$matchKey}->selected" => true]);
}

注意:时间参数必须用字符串格式传入,你示例中09:00未加引号会被识别为数字,导致匹配逻辑失效。


方案2:单SQL语句批量更新(仅支持MySQL 8.0+)

如果需要批量处理多条记录、不想做PHP层遍历,可以借助MySQL 8.0新增的JSON_TABLE函数实现动态路径匹配更新:

Appointment::where('doctor_id', $doctorId)
    ->where('day_id', $dayId)
    ->update([
        'time_slots' => DB::raw("
            JSON_SET(
                time_slots,
                CONCAT(
                    '$.',
                    (
                        SELECT jt.slot_key
                        FROM JSON_TABLE(
                            JSON_KEYS(time_slots),
                            '$[*]' COLUMNS (slot_key VARCHAR(50) PATH '$')
                        ) jt
                        WHERE JSON_UNQUOTE(JSON_EXTRACT(time_slots, CONCAT('$.', jt.slot_key, '.start'))) = ?
                        AND JSON_UNQUOTE(JSON_EXTRACT(time_slots, CONCAT('$.', jt.slot_key, '.end'))) = ?
                        LIMIT 1
                    ),
                    '.selected'
                ),
                true
            )
        ")
    ], [$targetStart, $targetEnd]);

避坑提示
  • 不要尝试用通配符直接更新,如果库中存在同时匹配起止时间的重复时段脏数据,通配逻辑会把所有匹配项的selected都改为true,不符合预约业务的唯一占用逻辑。
  • 如果你的MySQL版本低于8.0,不要使用方案2,JSON_TABLE是MySQL 8.0才新增的函数,低版本运行会直接报语法错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:27:18