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

Laravel路由:同一控制器方法中兼容主域名与子域名的多参数处理问题

Laravel路由:同一控制器方法中兼容主域名与子域名的多参数处理问题

我来帮你搞定这个头疼的问题!核心矛盾出在路由参数的传递/接收顺序不匹配,以及子域名参数无法直接设为可选这两点上,完全不需要重复写控制器方法,咱们用两种思路就能解决:


方案一:调整参数顺序+关联数组传参(快速修复)

1. 修正控制器方法的参数接收顺序

你当前的showByCategory方法把$category_slug放在了$subdomain前面,但子域名路由里的{subdomain}是路由的第一个参数,导致参数错位。把顺序反过来,并且利用Laravel的路由参数自动绑定,就不用手动从$request里取了:

public function showByCategory(Request $request, $subdomain = null, $category_slug)
{
    // 这里直接用$subdomain即可,不需要再$subdomain = $request->route('subdomain');
    $region_id = $this->regionService->getRegionIdByDomain($subdomain);
    
    // 剩下的逻辑保持不变...
    $categories = Category::whereNull('category_id')->get(['id', 'name', 'slug']);
    $regions = Region::with('districts')->get(['id', 'name', 'slug']);
    $services = Service::get(['id', 'name', 'slug', 'icon']);
    
    $category_id = Category::where('slug', $category_slug)->value('id');
    $sights = Sight::where('category_id', $category_id)->filter($request);
    
    if (!is_null($region_id)) {
        $sights = $sights->where('region_id', $region_id);
    }
    
    $sights = $sights->latest()->paginate(20);
    
    return view('main.sights.index', compact('sights', 'categories', 'regions', 'services'));
}

2. Blade模板用关联数组传参(避免顺序错误)

之前用顺序数组传参很容易踩坑,换成按参数名传参的关联数组,不管路由参数顺序怎么变都不会错:

@if(!is_null($subdomain))
<a href="{{ route('subdomain.main.sights.category.index', ['subdomain' => $subdomain, 'category_slug' => $sight->category->slug]) }}" class="btn btn-outline-weldonBlue">
    {{ $sight->category->name[app()->getLocale()] }}
</a>
@else
<a href="{{ route('main.sights.category.index', ['category_slug' => $sight->category->slug]) }}" class="btn btn-outline-weldonBlue">
    {{ $sight->category->name[app()->getLocale()] }}
</a>
@endif

3. 补全路由的语法错误

注意你主域名的路由代码里少了一个大括号,这会导致路由解析失败,赶紧补上:

Route::middleware(['language'])->group(function () {
    Route::domain('vizit.loc')->group(function () { // 这里补上缺失的{
        Route::controller(FrontSightController::class)->group(function () {
            Route::get('/sights', 'index')->name('main.sights.index');
            Route::get('/sights/category/{category_slug}', 'showByCategory')->name('main.sights.category.index');
        });
    });
});

方案二:用中间件注入子域名(更优雅的长期方案)

如果不想纠结参数顺序,可以写一个中间件,把subdomain参数统一注入到请求中,控制器直接从请求里拿就行:

1. 创建中间件

执行命令生成中间件:

php artisan make:middleware InjectSubdomain

然后编辑app/Http/Middleware/InjectSubdomain.php:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class InjectSubdomain
{
    public function handle(Request $request, Closure $next)
    {
        // 从路由里获取subdomain参数,没有则设为null
        $subdomain = $request->route('subdomain') ?? null;
        // 把subdomain存入请求属性
        $request->attributes->set('subdomain', $subdomain);
        
        return $next($request);
    }
}

2. 注册中间件

打开app/Http/Kernel.php,把中间件加到$routeMiddleware数组里:

protected $routeMiddleware = [
    // 其他中间件...
    'inject.subdomain' => \App\Http\Middleware\InjectSubdomain::class,
];

3. 更新路由组

把这个中间件加到你的语言中间件组里,或者直接加到两个路由组中:

// 子域名路由组
Route::middleware(['language', 'inject.subdomain'])->group(function () {
    Route::domain('{subdomain}.vizit.loc')->group(function () {
        Route::controller(FrontSightController::class)->group(function () {
            Route::get('/sights', 'index')->name('subdomain.main.sights.index');
            Route::get('/sights/category/{category_slug}', 'showByCategory')->name('subdomain.main.sights.category.index');
        });
    });
});

// 主域名路由组
Route::middleware(['language', 'inject.subdomain'])->group(function () {
    Route::domain('vizit.loc')->group(function () {
        Route::controller(FrontSightController::class)->group(function () {
            Route::get('/sights', 'index')->name('main.sights.index');
            Route::get('/sights/category/{category_slug}', 'showByCategory')->name('main.sights.category.index');
        });
    });
});

4. 简化控制器方法

现在控制器方法里不需要接收$subdomain参数了,直接从请求里拿:

public function showByCategory(Request $request, $category_slug)
{
    $subdomain = $request->get('subdomain');
    $region_id = $this->regionService->getRegionIdByDomain($subdomain);
    
    // 剩下的逻辑和之前一致...
}

这个方案的好处是,不管后续路由怎么调整,控制器都不需要改参数,逻辑更统一。


两种方案都能解决你的问题,方案一适合快速修复,方案二更适合长期维护。选一个你觉得顺手的就行!

备注:内容来源于stack exchange,提问作者SlimBoy Fat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 09:03:02