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
}
}
}
}
在这里我可以看到 $field
是 competition
,其中 $value
等于 premier-league
。
现在考虑一些问题:为什么父 class 应该是 return 子实例的那个?
基本上,如果我继续像这样实现它,resolveChildRouteBinding
将是 return CompetitionSeason
的那个,而我希望 [=] 上的解析逻辑19=] 本身决定要注入的实例。只有我一个吗?
感谢@lagbox 指出 resolveChildRouteBinding
的存在,无论出于何种原因我完全忽略了它(也许在文档中值得多一点 space?)
我这里有一个看似愚蠢的问题,但我找不到如何使用模型上的 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
}
}
}
}
在这里我可以看到 $field
是 competition
,其中 $value
等于 premier-league
。
现在考虑一些问题:为什么父 class 应该是 return 子实例的那个?
基本上,如果我继续像这样实现它,resolveChildRouteBinding
将是 return CompetitionSeason
的那个,而我希望 [=] 上的解析逻辑19=] 本身决定要注入的实例。只有我一个吗?
感谢@lagbox 指出 resolveChildRouteBinding
的存在,无论出于何种原因我完全忽略了它(也许在文档中值得多一点 space?)