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

Eloquent Sluggable未按路由参数作用域查询,获取错误Project记录

问题描述

我有两条路由:/vehicles/vehicle-one/projects/project-one 和 /vehicles/vehicle-two/projects/project-one。使用Eloquent Sluggable包处理Vehicle和Project模型的slug,为Project模型定义了以下方法,确保slug仅在所属车辆范围内唯一:

public function scopeWithUniqueSluggableConstraints(Builder $query, Model $model, $attribute, $config, $slug)
{
    $vehicle = $model->vehicle;

    return $vehicle ? $query->where('vehicle_id', $vehicle->getKey()) : null;
}

这个方法在创建Project记录时正常工作,但访问/vehicles/vehicle-two/projects/project-one时,获取到的却是vehicle-one下的project数据——系统只执行了Project::where('slug', $project->slug)并返回第一条记录,完全没考虑车辆的作用域。

目前我在控制器里用以下代码强制处理:

$project = Project::where('vehicle_id', $vehicle->id)->where('slug', $project->slug)->first();

请问能不能在RouteServiceProvider.php中处理这个问题,让控制器直接获取到正确的模型?


更新信息

路由定义如下:

Route::prefix('vehicles')->name('vehicles.')->group(function () {
    Route::get('/{vehicle}/projects/{project}', [ProjectController::class, 'show'])->name('projects.show');
});

ProjectController的show方法:

public function show(Vehicle $vehicle, Project $project)
{
    // $project 存储的是Vehicle One下slug为`project-one`的Project,而非Vehicle Two的
}

解决方案

可以在RouteServiceProvider里通过自定义路由模型绑定实现,让Laravel自动根据当前Vehicle实例筛选对应Project,具体步骤如下:

  • 在RouteServiceProvider的boot方法中添加自定义绑定逻辑:

    use App\Models\Vehicle;
    use App\Models\Project;
    use Illuminate\Support\Facades\Route;
    
    public function boot()
    {
        parent::boot();
    
        // 自定义project路由模型绑定,关联当前vehicle
        Route::bind('project', function ($slug) {
            // 获取当前路由中的vehicle实例
            $vehicle = Route::current()->parameter('vehicle');
            
            // 筛选该vehicle下对应slug的project,找不到则抛出404
            return $vehicle->projects()->where('slug', $slug)->firstOrFail();
        });
    }
    
  • 修改后控制器无需手动查询,直接拿到正确的Project实例:

    public function show(Vehicle $vehicle, Project $project)
    {
        // 此时$project就是当前$vehicle下对应slug的项目
        return view('projects.show', compact('vehicle', 'project'));
    }
    

原理说明

默认路由模型绑定仅依据slug全局查询,不会关联父级模型;自定义bind方法先获取路由中的vehicle参数,再通过关联关系查询该车辆下的project,确保返回当前车辆范围内的正确记录,firstOrFail()会自动抛出404异常,符合Laravel默认行为。

替代方案:在模型中处理绑定

也可以直接在Project模型中定义resolveRouteBinding方法,无需修改RouteServiceProvider:

use Illuminate\Database\Eloquent\ModelNotFoundException;

public function resolveRouteBinding($value, $field = null)
{
    $vehicle = request()->route('vehicle');
    
    if (!$vehicle) {
        throw new ModelNotFoundException();
    }
    
    $project = $this->where('vehicle_id', $vehicle->getKey())
                    ->where('slug', $value)
                    ->first();

    if (!$project) {
        throw new ModelNotFoundException();
    }

    return $project;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:00:15