Laravel Query Builder如何实现字段值toggle取反切换更新
Laravel Query Builder 实现字段状态切换的正确方案
原有写法的问题原因
- 传
'!active'时,传入的是PHP普通字符串,查询构造器会将其作为字面量参数绑定,不会解析为字段取反的SQL逻辑。如果active是tinyint(1)类型,MySQL会把非数字的字符串'!active'强制转换为数值0,因此只会出现1被改成0、0永远无法切到1的情况。 - 传
!'active'时,是在PHP层面执行逻辑非运算:非空字符串取反的结果固定为布尔值false,写入数据库时会被转成0,无论原字段值是什么都会被更新为0,自然无法实现切换效果。
正确实现方式
要让数据库识别到是对字段本身的值做运算,而非传入固定的绑定参数,需要使用SQL原生表达式传递运算逻辑,这种方式是单条SQL原子执行,没有并发竞态问题,性能也更优。
首先引入DB门面:
use Illuminate\Support\Facades\DB;
写法1:使用逻辑非运算(适用于0/1布尔类型字段)
生成的SQL为update password_movementsetactive= NOTactivewhereid = ?,完全符合取反预期:
PasswordMovement::whereId($id)->update([ 'active' => DB::raw('NOT active') ]);
写法2:使用算术运算(同样适用于0/1枚举值字段)
逻辑和上面完全等价,生成的SQL为set active = 1 - active:
PasswordMovement::whereId($id)->update([ 'active' => DB::raw('1 - active') ]);
注意:不要误用模型自带的
toggle()方法,该方法是用于多对多关联的中间表记录附加/解除操作,无法用来切换当前模型自身的字段值。
补充:模型实例写法(非原子操作,注意并发场景)
如果已经提前查询出了对应模型实例,也可以通过PHP层面取反后保存,但是这种方式需要先查库,高并发场景下可能出现竞态问题(两个请求同时读取到相同状态,重复更新):
$pm = PasswordMovement::find($id); $pm->active = !$pm->active; $pm->save();
内容的提问来源于stack exchange,提问作者poison pawn
相关产品推荐
相关产品推荐

