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

如何基于数据库同项目字段设置Laravel模型自增版本属性?

Laravel 11实现Text模型自动生成版本号方案

需求回顾

在使用Laravel 11和PostgreSQL的项目中,texts表的version字段需要满足:

  • 插入新记录时,自动设为同项目下已有text的最大version值加1
  • 更新记录时,不修改version值
  • 优先用Eloquent逻辑实现,避免数据库触发器/函数

方案一:重写模型的save方法

直接在Text模型中重写save方法,区分创建和更新场景:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Text extends Model
{
    // 根据实际业务调整可批量赋值的字段
    protected $fillable = ['content', 'user_id', 'project_id', 'words', 'processed'];

    public function save(array $options = [])
    {
        // 仅在创建新记录时计算version
        if (!$this->exists) {
            // 获取同项目的最大版本号,无记录时默认设为1
            $maxVersion = static::where('project_id', $this->project_id)->max('version');
            $this->version = $maxVersion ? $maxVersion + 1 : 1;
        }

        return parent::save($options);
    }
}

关键细节说明

  • $this->exists:Eloquent模型自带属性,判断当前模型是否已存入数据库,仅新模型(创建场景)会触发版本计算
  • 避免硬编码表名:使用static::where()调用当前模型的查询构建器,无需手动写texts表名
  • 空值处理:当项目下无任何text记录时,max('version')返回null,此时version直接设为1

方案二:使用模型creating事件(更优雅)

利用Laravel的模型事件机制,仅在创建新记录前触发版本计算,更符合Laravel的设计理念:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Text extends Model
{
    protected $fillable = ['content', 'user_id', 'project_id', 'words', 'processed'];

    protected static function booted()
    {
        // 绑定creating事件:仅在创建新记录时执行
        static::creating(function (Text $text) {
            $maxVersion = static::where('project_id', $text->project_id)->max('version');
            $text->version = $maxVersion ? $maxVersion + 1 : 1;
        });
    }
}

优势

  • 无需重写save方法,避免和其他自定义save逻辑冲突
  • 事件驱动的方式耦合度更低,逻辑更清晰

并发问题处理(可选)

如果系统存在高并发创建同项目text的场景,可能会出现版本号重复的情况。可以通过以下方式解决:

  1. 添加数据库唯一索引
    在texts表的迁移文件中,给project_id和version组合添加唯一索引:
Schema::table('texts', function (Blueprint $table) {
    $table->unique(['project_id', 'version']);
});
  1. 捕获冲突异常并重试
    修改模型逻辑,捕获PostgreSQL的唯一约束冲突错误(错误码23505),自动重试版本号计算:
// 以方案一为例,修改save方法
public function save(array $options = [])
{
    if (!$this->exists) {
        $attempts = 3; // 设置重试次数
        while ($attempts--) {
            $maxVersion = static::where('project_id', $this->project_id)->max('version');
            $this->version = $maxVersion ? $maxVersion + 1 : 1;
            
            try {
                return parent::save($options);
            } catch (\Illuminate\Database\QueryException $e) {
                // 仅处理唯一约束冲突异常
                if ($e->getCode() !== '23505') {
                    throw $e;
                }
                // 重试次数耗尽则抛出异常
                if ($attempts === 0) {
                    throw new \Exception('无法生成唯一版本号,请稍后重试');
                }
            }
        }
    }

    return parent::save($options);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:32:25