Laravel:将字段传递给路由定义中的 resolveRouteBinding

Laravel: pass field to resolveRouteBinding from route definition

我这里有一个看似愚蠢的问题,但我找不到如何使用模型上的 resolveRouteBinding 方法定义显式模型绑定解析逻辑。

我设法写下了它基于 field 参数值的逻辑,但我似乎无法弄清楚如何在定义路由时将值传递给该参数。

谁能帮帮我?

编辑: 正确的问题是如何告诉框架将什么值传递给 field 参数?

编辑 2:这是我在运气不好的情况下所做的示例。

基本上我想实现一个自定义逻辑来检索表示多对多关系的 class。

这些是 master classes.

class Competition extends Model {
    // ...

    /**
     * Get the editions of the competition.
     */
    public function competitionSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }
}
class Season extends Model {
    // ...

    /**
     * Get the editions of the competitions in this season
     */
    public function competitionsSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }
}

这是关系class。

class CompetitionSeason extends Model {
    // ...

    /**
     * Get the competition of this competition edition.
     */
    public function competition() {
        return $this->belongsTo(Competition::class);
    }

    /**
     * Get the season of this competition edition.
     */
    public function season() {
        return $this->belongsTo(Season::class);
    }

    public function resolveRouteBinding($value, $field = null) {
        error_log("[resolveRouteBinding] Field: " . $field);

        // TODO: make the query
    }

    public function resolveChildRouteBinding($childType, $value, $field = null) {
        error_log("[resolveChildRouteBinding] Field: " . $field);

        // TODO: make the query
    }
}

我现在想要的是在控制器中注入一个 CompetitionSeason 作为 Season 下的嵌套资源(以及稍后在 Competition 下)。

这就是路由的定义方式。

// api.php

Route::apiResource('seasons.competitionSeasons', MockController::class)->scoped([
    'competitionSeason' => 'competition'
]);

因为我在路线上使用 slug 而不是 ID,所以 competition 任何 resolveBinding 方法都需要这里来导航关系并从 Competition实例.

基本上,请求是这样的:

GET .../api/seasons/19-20/competitionSeasons/premier-league

其中 premier-league 是引用 Competition 的 slug,基本上是多对多关系的另一端。

这是控制器..现在完全没用。

class MockController extends Controller {

    public function show(Season $season, CompetitionSeason $competitionSeason) {
        error_log($competitionSeason);
    }
}

我知道如何实现缺少的逻辑,我非常清楚,我想看到的是当框架尝试解析 CompetitionSeason 的所需实例时打印的 $field这样我就可以获得相关资源的 slug,并知道当 CompetitionSeason 也将成为 Competition 的子资源时要导航的关系。

当您通过路由定义自定义用于隐式路由模型绑定的密钥时:

Route::get('users/{user:name}', ...);

'name' 最终将作为 $field 传递给该方法调用。

来自Illuminte\Database\Eloquent\Model

public function resolveRouteBinding($value, $field = null)
{
    return $this->resolveRouteBindingQuery($this, $value, $field)->first();
}

public function resolveRouteBindingQuery($query, $value, $field = null)
{
    return $query->where($field ?? $this->getRouteKeyName(), $value);
}

Laravel 8.x Docs - Routing - Route Model Binding - Implicit Bindings - Customizing the Key

更新:

如果您需要使用资源路由创建此自定义绑定,您可以在路由定义上使用 scoped 方法:

Route::resource('users', Controller::class)
    ->scoped(['user' => 'name']);

您也可以直接在 resource 上使用 $options 参数(第 3 个参数):

Route::resource('users', Controller::class, [
    'bindingFields' => [
        'user' => 'name'
    ]
]);

我知道它应该如何工作,尽管我不知道为什么会这样..(下面的一些考虑)。

基本上我应该覆盖的public function resolveChildRouteBinding,所以在这种情况下Season

所以现在 Season 看起来像这样:

class Season extends Model {
    // ...

    public function competitionsSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }


    public function resolveChildRouteBinding($childType, $value, $field = null) {
        error_log("[resolveChildRouteBinding] Field: " . $field);

        if ($childType == 'competitionSeason') {
            if ($field == 'competition' {
                // TODO: make the query
            }
        }
    }
}

在这里我可以看到 $fieldcompetition,其中 $value 等于 premier-league

现在考虑一些问题:为什么父 class 应该是 return 子实例的那个?

基本上,如果我继续像这样实现它,resolveChildRouteBinding 将是 return CompetitionSeason 的那个,而我希望 [=] 上的解析逻辑19=] 本身决定要注入的实例。只有我一个吗?

感谢@lagbox 指出 resolveChildRouteBinding 的存在,无论出于何种原因我完全忽略了它(也许在文档中值得多一点 space?)