如何在Laravel(OctoberCMS)查询中设置字段自增?为何自增量为预期值2倍?
固定双倍增量的成因
首先要明确:increment()方法是基于单条原子SQL实现的字段自增,单次调用只会执行一次UPDATE 表名 SET 字段值 = 字段值 + 增量 WHERE 条件,不存在自动重复执行的机制。固定出现“传1加2、传2加4”的2倍增量,本质就是onStart()钩子被同一次页面请求重复触发了2次,常见的触发场景集中在几个易忽略的点:
- 布局与页面的钩子重复挂载:OctoberCMS的渲染流程中,布局的
onStart()逻辑会先于页面本身的onStart()逻辑执行,如果不小心在布局文件和页面文件里都写了相同的访客统计代码,就会在同一次请求里连续执行两次increment,刚好凑出2倍的增量。 - URL规则的自动重定向触发:如果站点配置了尾部斜杠自动重定向、或者HTTPS/HTTP自动跳转规则,用户感知不到的301/302跳转也会产生两次独立的路由请求,两次请求都会匹配到同一个页面,各执行一次自增逻辑。
- 路由匹配的兜底规则覆盖:如果配置了过于宽泛的通用路由,浏览器发起的无关资源请求(比如favicon探测、资源预取请求)被错误匹配到了当前内容页的路由规则,也会额外触发一次页面钩子的执行。
正确实现自增逻辑的方案
第一步:先定位重复触发的根源
在onStart()中加入请求日志,记录每次触发的上下文,就能快速定位重复原因:
function onStart(){ \Log::info('访客统计逻辑触发', [ '请求地址' => request()->fullUrl(), '请求参数' => request()->all(), '路由参数slug' => $this->param('slug'), '触发时间' => microtime(true) ]); ModelName::where('slug', '=', $this->param('slug'))->increment('visitors'); }
刷新一次页面后查看日志,如果看到两条触发记录,就顺着记录里的请求信息排查上面提到的几个常见重复点即可。
第二步:加请求级防重复加固
哪怕排查完所有重复触发的场景,也可以加一层兜底逻辑,避免同一次请求内的钩子重复执行导致计数异常:
function onStart(){ $slug = $this->param('slug'); // 使用array缓存驱动做单次请求内的标记,请求结束后自动清空 $requestCache = \Cache::store('array'); if (!$requestCache->has("visitor_increment_{$slug}")) { ModelName::where('slug', '=', $slug)->increment('visitors'); $requestCache->put("visitor_increment_{$slug}", true); } }
这种方式不需要修改现有路由或者配置,就能从逻辑上保证同一次请求不管触发多少次onStart(),都只会执行一次自增操作。
可选:优化统计逻辑避免刷新刷量
如果要避免用户频繁刷新页面导致的虚高计数,可以加基于用户会话的短时间锁定逻辑,同一个用户访问同一条内容,在设定的时间窗口内只计一次数:
function onStart(){ $slug = $this->param('slug'); $lockKey = "visitor_count_{$slug}_" . session()->getId(); // 同一个用户1分钟内重复访问不重复计数 if (!\Cache::has($lockKey)) { ModelName::where('slug', '=', $slug)->increment('visitors'); \Cache::put($lockKey, true, now()->addMinute()); } }
内容的提问来源于stack exchange,提问作者imperator23
相关产品推荐
相关产品推荐

