laravel 9 具有多个可选参数的路由模型绑定
laravel 9 route model binding with multiple optional parameters
我定义了这条路线:
Route::get('/categories/{category}/{country}/{state?}/{county?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
public function show(Category $category, Country $country, State $state = null, County $county = null, City $city = null) {
echo 'ok';
}
很好,自动检查关系,使用 1、2 或 3 个可选参数。但是......我想扩展它,以便 COUNTY 并不总是强制性的。因为有些城市与州有直接关系,中间没有 county_id。城市 table 有 county_id 和 state_id,并且总是只有其中一个存在。如果我添加:
Route::get('/categories/{category}/{country}/{state?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
只有一条路线有效。
我该如何解决这个问题?谢谢
您可以定义两条单独的路线
Route::get(
'/categories/{category}/{country}/{state?}/{county?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county');
Route::get(
'/categories/{category}/{country}/{state?}/{county?}/{city?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county.city');
然后在控制器中检查路由名称
public function show(
Category $category,
Country $country,
State $state = null
) {
if(
request()->route()->named('categories.show.county') &&
request()->route()->hasParameter('county')
) {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->named('categories.show.county.city')) {
if(request()->route()->hasParameter('county') {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->hasParameter('city')) {
$city = City::where(
(new City)->getRouteKeyName(),request()->route('city')
)
->firstOrFail();
}
}
}
所以,根据 Donkarnash 的回答,我终于找到了解决方案。
Route::get('/categories/{category}/{country}/{state?}/{county_slug?}/{city_slug?}',
['App\Http\Controllers\LocationController', 'show'])->scopeBindings();
public function show(
Category $category,
Country $country,
State $state = null,
$county_slug = null,
$city_slug = null
) {
if ($county_slug && $city_slug)
{
// two parameters present, that means the chain is state -> county -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->firstOrFail();
$city = City::where('county_id', $county->id)
->where('slug', $city_slug)
->firstOrFail();
} else {
if ($county_slug) {
// one parameter present, that means the chain is state -> county OR state -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->first();
if (!$county) {
$city_slug = $county_slug;
$city = City::where('state_id', $state->id)
->where('slug', $city_slug)
->first();
}
if (!$county && !$city) {
abort(404);
}
}
}
}
然后在迁移状态 table:
$table->unique(['slug', 'country_id']);
县table:
$table->unique(['slug', 'state_id']);
城市table:
$table->unique(['slug', 'state_id']);
$table->unique(['slug', 'county_id']);
并且有效。唯一的缺点是,如果有属于同一州的县和市具有相同的鼻涕虫。例如,带有 slug“test”和 state_id“15”的县以及带有 slug“test”和 state_id“15”的城市。那么它将无法正常工作并导致县。但通常一个国家/地区的所有城市都有一个国家链 -> 州 -> 县 -> 市或国家 -> 州 -> 市,因此这个小缺点不会影响网站的最终结果。尽管如此,这也可以通过调整请求验证规则来解决。
我定义了这条路线:
Route::get('/categories/{category}/{country}/{state?}/{county?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
public function show(Category $category, Country $country, State $state = null, County $county = null, City $city = null) {
echo 'ok';
}
很好,自动检查关系,使用 1、2 或 3 个可选参数。但是......我想扩展它,以便 COUNTY 并不总是强制性的。因为有些城市与州有直接关系,中间没有 county_id。城市 table 有 county_id 和 state_id,并且总是只有其中一个存在。如果我添加:
Route::get('/categories/{category}/{country}/{state?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
只有一条路线有效。
我该如何解决这个问题?谢谢
您可以定义两条单独的路线
Route::get(
'/categories/{category}/{country}/{state?}/{county?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county');
Route::get(
'/categories/{category}/{country}/{state?}/{county?}/{city?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county.city');
然后在控制器中检查路由名称
public function show(
Category $category,
Country $country,
State $state = null
) {
if(
request()->route()->named('categories.show.county') &&
request()->route()->hasParameter('county')
) {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->named('categories.show.county.city')) {
if(request()->route()->hasParameter('county') {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->hasParameter('city')) {
$city = City::where(
(new City)->getRouteKeyName(),request()->route('city')
)
->firstOrFail();
}
}
}
所以,根据 Donkarnash 的回答,我终于找到了解决方案。
Route::get('/categories/{category}/{country}/{state?}/{county_slug?}/{city_slug?}',
['App\Http\Controllers\LocationController', 'show'])->scopeBindings();
public function show(
Category $category,
Country $country,
State $state = null,
$county_slug = null,
$city_slug = null
) {
if ($county_slug && $city_slug)
{
// two parameters present, that means the chain is state -> county -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->firstOrFail();
$city = City::where('county_id', $county->id)
->where('slug', $city_slug)
->firstOrFail();
} else {
if ($county_slug) {
// one parameter present, that means the chain is state -> county OR state -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->first();
if (!$county) {
$city_slug = $county_slug;
$city = City::where('state_id', $state->id)
->where('slug', $city_slug)
->first();
}
if (!$county && !$city) {
abort(404);
}
}
}
}
然后在迁移状态 table:
$table->unique(['slug', 'country_id']);
县table:
$table->unique(['slug', 'state_id']);
城市table:
$table->unique(['slug', 'state_id']);
$table->unique(['slug', 'county_id']);
并且有效。唯一的缺点是,如果有属于同一州的县和市具有相同的鼻涕虫。例如,带有 slug“test”和 state_id“15”的县以及带有 slug“test”和 state_id“15”的城市。那么它将无法正常工作并导致县。但通常一个国家/地区的所有城市都有一个国家链 -> 州 -> 县 -> 市或国家 -> 州 -> 市,因此这个小缺点不会影响网站的最终结果。尽管如此,这也可以通过调整请求验证规则来解决。