You need to enable JavaScript to run this app. 如何使用Fatfree框架ORM实现带状态校验的条件更新操作
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Fatfree框架ORM实现带状态校验的条件更新操作

问题根因

你现有代码的核心问题是原生UPDATE和ORM的save()是两个独立操作:就算UPDATE没有匹配到任何行(影响行数为0),SQL本身执行是成功的,不会自动触发事务回滚。后续的save()方法会直接以主键ID为更新条件,把内存中已经修改过的对象全字段写入数据库,直接覆盖掉stateA的校验逻辑,等于你写的状态条件完全没生效。

实现方案

方案1:使用F3 ORM自带的条件更新(推荐)

F3的ORM模型继承自Cursor类,update()方法支持传入自定义WHERE条件,同时会返回受影响的行数,你可以根据返回值判断更新是否符合预期,失败就直接回滚事务。

$instance = $f3->get('domainObject')->load(['id=?', $id]);
if (!$instance->loaded()) {
    throw new \Exception('对象不存在');
}
$originalState = $instance->stateA;
$stateMachine = new StateMachine($instance, $config);
$stateMachine->apply($body['state']);

$db->begin();
try {
    // 传入状态校验条件,update方法返回受影响行数
    $affectedRows = $instance->update(['id=? AND stateA=?', $id, $originalState]);
    if ($affectedRows !== 1) {
        throw new \Exception('状态已被修改,更新失败');
    }
    $db->commit();
} catch (\Exception $e) {
    $db->rollback();
    throw $e;
}

方案2:保留原生SQL,删除冗余save操作

如果你习惯写原生SQL,完全不需要额外调用save()方法,直接判断UPDATE语句的影响行数即可:

$instance = $f3->get('domainObject')->load(['id=?', $id]);
if (!$instance->loaded()) {
    throw new \Exception('对象不存在');
}
$originalState = $instance->stateA;
$stateMachine = new StateMachine($instance, $config);
$newState = $stateMachine->apply($body['state']);

$db->begin();
try {
    $affectedRows = $db->exec(
        'UPDATE domainObject SET stateA=? WHERE id=? AND stateA=?',
        $newState,
        $id,
        $originalState
    );
    if ($affectedRows !== 1) {
        throw new \Exception('状态已被修改,更新失败');
    }
    // 同步更新内存对象状态,避免后续逻辑取到旧值
    $instance->stateA = $newState;
    $db->commit();
} catch (\Exception $e) {
    $db->rollback();
    throw $e;
}

补充建议

  • 可以把这个状态校验逻辑封装到模型的beforeUpdate钩子中,业务层调用save()时自动加状态条件,复用性更高
  • 状态机的apply()校验建议放在事务内执行,避免校验通过后、事务提交前数据被其他请求修改的极端并发问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 06:57:02