You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel 7实现用户单次测试限制及视图切换功能需求求助

解决方案:限制Laravel 7用户仅完成一次测试

要实现仅注册用户可参与、且每个用户只能完成一次测试的需求,我们可以通过「检查用户已有测试结果」的方式来实现,同时要兼顾前端访问限制和后端提交验证,防止用户绕过限制。

步骤1:确保User模型与Result模型的关联正确

首先,你的User模型需要和Result模型建立一对一关联(因为每个用户只能有一个测试结果),打开app/User.php添加关联:

public function userResults()
{
    return $this->hasOne(Result::class);
}

注:如果之前用的是hasMany,也能正常工作,但hasOne更贴合「仅一次测试」的业务逻辑。

步骤2:修改TestController的index方法,拦截已完成测试的用户

在测试页面的入口方法里,先检查当前登录用户是否已经有测试结果,如果有就直接返回「已完成测试」的视图:

public function index() { 
    // 核心:检查用户是否已有测试结果
    if (auth()->user()->userResults()->exists()) {
        // 返回自定义的已完成提示视图,比如client.test_completed
        return view('client.test_completed');
    }

    $categories = Category::with(['categoryQuestions' => function ($query) { 
        $query->orderBy('id') ->with(['questionOptions' => function ($query) { 
            $query->orderBy('id'); 
        }]); 
    }]) ->whereHas('categoryQuestions') ->get(); 
    return view('client.test', compact('categories')); 
}

步骤3:在store方法中添加二次验证,防止恶意提交

即使前端拦截了,也可能有用户通过直接POST请求绕过限制,所以在提交测试结果的方法里也要加检查:

public function store(StoreTestRequest $request) { 
    // 二次验证:确保用户未完成过测试
    if (auth()->user()->userResults()->exists()) {
        return redirect()->route('client.test.index')
            ->with('error', '你已经完成过这个测试,无法再次提交');
    }

    $options = Option::find(array_values($request->input('questions'))); 
    $result = auth()->user()->userResults()->create([ 
        'total_points' => $options->sum('points') 
    ]); 
    $questions = $options->mapWithKeys(function ($option) { 
        return [$option->question_id => [ 
            'option_id' => $option->id, 
            'points' => $option->points 
        ] ]; 
    })->toArray(); 
    $result->questions()->sync($questions); 
    return redirect()->route('client.results.show', $result->id); 
}

步骤4:创建「已完成测试」的视图

resources/views/client/目录下新建test_completed.blade.php,添加提示内容:

<div class="container mt-5">
    <div class="card">
        <div class="card-body text-center">
            <h2 class="card-title">测试已完成</h2>
            <p class="card-text">你已经参与过这个测试,无法再次进行。</p>
            <!-- 可以添加查看结果的链接 -->
            <a href="{{ route('client.results.show', auth()->user()->userResults->id) }}" class="btn btn-primary mt-3">查看我的测试结果</a>
        </div>
    </div>
</div>

进阶:用中间件封装检查逻辑(可选)

如果后续有多个路由需要限制用户重复测试,可以把检查逻辑抽成中间件,让代码更简洁:

  1. 创建中间件:
php artisan make:middleware PreventDuplicateTest
  1. 编辑中间件app/Http/Middleware/PreventDuplicateTest.php
public function handle($request, Closure $next)
{
    // 仅对登录用户检查
    if (auth()->check() && auth()->user()->userResults()->exists()) {
        return redirect()->route('client.test.index');
        // 或者直接返回已完成视图:return view('client.test_completed');
    }

    return $next($request);
}
  1. app/Http/Kernel.php中注册中间件:
protected $routeMiddleware = [
    // ... 其他中间件
    'prevent.duplicate.test' => \App\Http\Middleware\PreventDuplicateTest::class,
];
  1. 在路由中使用中间件:
Route::group(['middleware' => ['auth', 'prevent.duplicate.test']], function () {
    Route::get('/test', 'TestController@index')->name('client.test.index');
    Route::post('/test', 'TestController@store')->name('client.test.store');
});

这样就完整实现了「用户仅能完成一次测试,已完成用户访问测试页加载专属视图」的需求。

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

火山引擎 最新活动