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

Laravel在线测验填写进度持久化:最佳方案及会话留存问题咨询

存储用户测验进度的最佳方案(Laravel实现)

嘿,我来帮你理清这个问题——你要的是让用户哪怕一周后回来还能继续编辑测验进度,首先得敲黑板:Session(哪怕是DB Session)天生是为短期会话设计的,Laravel登出时会自动销毁对应会话记录,所以靠Session实现长期留存根本行不通。最可靠的方案是直接把进度持久化到数据库,和用户账号绑定。

一、推荐方案:数据库专属表存储

这是最稳妥的做法,完全不受会话状态影响,步骤也很清晰:

1. 创建进度存储表

先生成一个专门的表来存测验进度,比如quiz_progress,字段要覆盖这些关键信息:

  • user_id:关联用户表的外键(绑定到具体用户)
  • quiz_id:关联测验表的外键(如果有多个不同测验)
  • progress_data:用JSON类型存用户填写的答案(Laravel对JSON字段支持很好)
  • updated_at:自动记录最后修改时间,方便后续清理过期进度

生成迁移文件的命令:

php artisan make:migration create_quiz_progress_table

迁移文件示例代码:

public function up()
{
    Schema::create('quiz_progress', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->onDelete('cascade');
        $table->foreignId('quiz_id')->constrained()->onDelete('cascade');
        $table->json('progress_data');
        $table->timestamps();
    });
}

运行迁移:

php artisan migrate

2. 模型与业务逻辑实现

创建对应的QuizProgress模型,然后写简单的保存/获取进度的方法:

// app/Models/QuizProgress.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class QuizProgress extends Model
{
    protected $fillable = ['user_id', 'quiz_id', 'progress_data'];
    protected $casts = ['progress_data' => 'array']; // 自动把JSON转成数组
}

然后在控制器或服务类里写业务逻辑:

// 保存/更新用户进度
public function saveProgress(User $user, Quiz $quiz, array $currentAnswers)
{
    QuizProgress::updateOrCreate(
        ['user_id' => $user->id, 'quiz_id' => $quiz->id],
        ['progress_data' => $currentAnswers]
    );
}

// 获取用户之前的进度
public function getSavedProgress(User $user, Quiz $quiz)
{
    $progress = QuizProgress::where('user_id', $user->id)
        ->where('quiz_id', $quiz->id)
        ->first();
    
    return $progress ? $progress->progress_data : [];
}

这样用户不管什么时候登录,都能拉取到自己的进度,还可以加个定时任务清理超过一周的过期数据:

// 在App\Console\Kernel.php的schedule方法里添加
protected function schedule(Schedule $schedule)
{
    // 每周清理一次一周前的过期进度
    $schedule->call(function () {
        QuizProgress::where('updated_at', '<', now()->subWeek())->delete();
    })->weekly();
}

二、如果非要用DB Session实现登出不销毁?

虽然真心不推荐,但如果有特殊场景要这么做,你可以自定义登出逻辑,跳过Laravel默认的会话销毁步骤:

  1. 创建自定义登出控制器:
php artisan make:controller Auth/CustomLogoutController
  1. 编写登出逻辑,只清除认证状态,保留会话:
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CustomLogoutController extends Controller
{
    public function __invoke(Request $request)
    {
        // 只清除用户登录状态,不销毁会话
        Auth::logout();
        
        // 刷新CSRF令牌,避免安全问题
        $request->session()->regenerateToken();
        
        return redirect('/');
    }
}
  1. 替换默认的登出路由:
// routes/web.php
use App\Http\Controllers\Auth\CustomLogoutController;

// 替换默认的logout路由
Route::post('/logout', CustomLogoutController::class)->name('logout');

⚠️ 注意:这种方式会导致会话一直存在,不仅浪费存储,还可能出现多设备登录时的状态混乱,所以非常不推荐。

三、可选的实用包

如果不想自己写底层逻辑,可以考虑这些Laravel生态的包:

  • spatie/laravel-data:可以帮你结构化存储进度数据,让JSON字段的操作更优雅
  • laravel/redis:如果想用Redis存进度(比数据库读写更快),可以用Laravel自带的Redis缓存,关联用户ID存进度

不过说实话,自己写数据库存储的逻辑已经足够简单,包更多是锦上添花,核心思路还是绑定用户的持久化存储。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:54:05