Laravel 自增时不自动更新 updated_at:原因与处理方式

认真发呆
认真发呆
发布时间:2026-08-25| 74人浏览过
原创

前言

在 Laravel 项目里,浏览量、点赞数这类字段常用 increment() 处理,但不少人会在上线后才发现:数值确实变了,updated_at 也一起被刷新。这个问题不复杂,关键在于弄清 Eloquent 更新时的判断条件,再针对单次自增场景做一层轻量控制,避免把真正的内容修改时间搅乱。

文章浏览量、点赞数这类字段做自增时,很多人都会直接调用 Laravel 的 increment()。但在实际项目里,这种写法往往会顺手把 updated_at 一起改掉,结果导致“内容最后更新时间”失真。下面就从 Eloquent 的判断逻辑入手,看看它为什么会更新这个字段,以及怎样在不改动整体模型设计的前提下,让某次自增跳过时间戳更新。

为什么 increment() 会改动 updated_at

问题最常见的场景,是文章浏览量自增:

Laravel 自增触发 updated_at 的判断链路示意图
自增为什么会带出 updated_at用编辑卡片和箭头说明 `increment()` 为什么会连带更新 `updated_at`。
$article->increment('views', 1);

这段代码看起来只是把 views 加 1,但实际执行后,Laravel 还会自动更新 updated_at。如果这个字段在你的业务里代表“文章内容最近修改时间”,那浏览一次就刷新一次显然不合理。

继续往下看源码,可以发现 Eloquent 在更新时会先判断当前模型是否启用了时间戳机制。相关逻辑在源码中大致如下:

    // vendorlaravelframeworksrcIlluminateDatabaseEloquentBuilder.php

    /**
     * Add the "updated at" column to an array of values.
     *
     * @param  array  $values
     * @return array
     */
    protected function addUpdatedAtColumn(array $values)
    {
        if (! $this->model->usesTimestamps() ||
            is_null($this->model->getUpdatedAtColumn())) {
            return $values;
        }

        $column = $this->model->getUpdatedAtColumn();

        $values = array_merge(
            [$column => $this->model->freshTimestampString()],
            $values
        );

        $segments = preg_split('/s+ass+/i', $this->query->from);

        $qualifiedColumn = end($segments).'.'.$column;

        $values[$qualifiedColumn] = $values[$column];

        unset($values[$column]);

        return $values;
    }

这里的关键判断是 usesTimestamps()。只要模型启用了时间戳,并且定义了 updated_at 字段,Laravel 就会把新的时间写进去。

源码里真正的判断依据是什么

再看 usesTimestamps() 的实现,会更直接一些:

    /**
     * Determine if the model uses timestamps.
     *
     * @return bool
     */
    public function usesTimestamps()
    {
        return $this->timestamps;
    }

也就是说,这个行为最终取决于模型上的 timestamps 属性。只要它为 true,类似 increment() 这样的更新操作就会带上 updated_at。

这也解释了为什么很多人会遇到这个问题:模型本身通常需要时间戳,不能简单地把 public $timestamps = false; 永久关掉;但某些特定操作,比如浏览量、热度值、计数器更新,又确实不希望触发更新时间变化。

一个更适合业务场景的处理方式

既然判断条件是 timestamps,那思路就很明确了:只在当前这次操作前临时把它关掉。为了不把这段逻辑散落在各个模型里,可以抽成一个 trait:

Laravel 通过 trait 临时关闭 timestamps 的实现思路图
trait 方案怎么落地把临时关闭时间戳的处理封装进 trait,适合浏览量、点赞数等计数字段。
<?php

namespace AppTraits;

trait UpdateWithoutUsesTimestamps
{
    /**
     * Determine if the model uses timestamps.
     */
    public function notUsesTimestamps()
    {
        $this->timestamps = false;

        return $this;
    }
}

这个 trait 做的事情很简单:提供一个 notUsesTimestamps() 方法,在链式调用前把当前模型实例的 timestamps 设为 false。

然后在对应的模型中引入它:

<?php

namespace App;

use AppTraitsUpdateWithoutUsesTimestamps;
use IlluminateDatabaseEloquentModel;

class Article extends Model
{
    use UpdateWithoutUsesTimestamps;
}

这样一来,模型默认仍然保留 Laravel 的时间戳行为,只有在你明确调用这个方法时,才会跳过本次更新时间写入。

自增时应该怎么调用

实际使用时,把原来的自增写法替换成下面这样即可:

$article->notUsesTimestamps()->increment('views', 1);

这样执行后,views 会正常加 1,但不会自动更新 updated_at。

这个方案比较适合以下场景:

  • 文章浏览量自增
  • 点赞数、收藏数、评论计数更新
  • 不应影响“内容最后修改时间”的统计字段维护

如果你的业务需要严格区分“内容编辑”与“行为计数变化”,这种做法会比直接关闭整个模型的时间戳更稳妥,也更容易控制影响范围。

使用这种写法时要注意什么

这个思路的优点是轻量、直接,几乎不需要改动原有模型结构;但它本质上还是通过修改模型实例上的 timestamps 属性来影响行为,因此更适合这种单次、明确的调用场景。

换句话说,它解决的是“某一次更新不想动 updated_at”的问题,而不是彻底改变模型的时间戳策略。对于资讯、内容管理、博客系统这类项目,这样的粒度通常已经够用:正常编辑文章时保留 updated_at,只有浏览量这类计数字段变化时才跳过更新,语义上也更清晰。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。