這是用戶在 2025-6-9 4:41 為 https://tenancy-v4.pages.dev/routing/#central-routes 保存的雙語快照頁面,由 沉浸式翻譯 提供雙語支持。了解如何保存?
Skip to content
跳至內容

Routing  路由

This page goes over the different ways to handle route registration in multi-tenant applications, as well as common obstacles.
本頁介紹多租戶應用程式中處理路由註冊的不同方法,以及常見的障礙。

Basics  基礎知識

After following the Getting started guide, you can register your central routes in routes/web.php — like in a normal, single-tenant application — and your tenant routes in routes/tenant.php.
在完成入門指南後,您可以在 routes/web.php 中註冊您的中央路由 — 就像在一般的單租戶應用程式中一樣 — 並在 routes/tenant.php 中註冊您的租戶路由。

routes/web.php
Route::get('/', CentralHomeController::class);
Route::get('/purely-central-route', ...);
routes/tenant.php
Route::middleware([
'web',
InitializeTenancyByDomain::class,
PreventAccessFromUnwantedDomains::class,
])->group(function () {
Route::get('/', TenantHomeController::class);
Route::get('/purely-tenant-route', ...);
});

Conflicting routes  路由衝突

This was briefly touched upon on the Getting started page.
這在入門頁面中有簡要提及。

Conflicting routes refers to the fact that you may want to have two different routes on the same path — one in the central part of the application and one in the tenant part of the application.
路由衝突是指您可能想在相同路徑上設置兩個不同的路由 — 一個位於應用程式的中央部分,另一個位於租戶部分。

The only way to solve this with Laravel’s routing is to one of these routes to a specific domain. That way, the route won’t be used when the domain condition isn’t met.
解決這個問題的唯一方法是將其中一條路由指定到特定的網域。這樣,當網域條件不符合時,該路由就不會被使用。

Specifically, this means registering your central routes only on central domains:
具體來說,這表示你只在中央網域上註冊你的中央路由:

routes/web.php
Route::domain('my-central-domain.com')->group(function () {
// your central routes
});

That way, you will be able to have two different / routes in routes/web.php and routes/tenant.php, since one of them will be defined only on a specific domain.
這樣,你就能在 routes/web.phproutes/tenant.php 中擁有兩條不同的 / 路由,因為其中一條只會定義在特定的網域上。

Central routes  中央路由

Continuing the previous section, to register central routes, you should bind their definition to your central domain.
接續前一節,註冊中央路由時,應將其定義綁定到你的中央網域。

However, this shouldn’t be hardcoded as in the previous example. The domains should be read from your central domains config:
不過,這不應該像前一個範例那樣硬編碼。網域應該從你的中央網域設定中讀取:

foreach (config('tenancy.identification.central_domains') as $domain) {
Route::domain($domain)->group(function () {
// your central routes
});
}

This configuration key is important, and you should make sure its value is accurate (it defaults to a domain extracted from your APP_URL). It’s used by the tenancy middleware to handle the counterpart of this: preventing access to tenant routes from central domains.
這個設定鍵非常重要,您應該確保其值是正確的(預設為從您的 APP_URL 中擷取的網域)。它被租戶中介軟體用來處理相對應的部分:防止從中央網域存取租戶路由。

Tenant routes aren’t registered using domain(), since tenant domains are dynamic — they’re read from the database and can change any time. Central domains are static, so they’re the better candidate for domain(). In short: we have two conflicting routes, so at least one of them needs to be bound to a specific domain. It won’t work for the tenant domains since they’re dynamic, so we do this with the central routes.
租戶路由不會使用 domain() 來註冊,因為租戶網域是動態的——它們是從資料庫讀取,且隨時可能改變。中央網域是靜態的,因此它們比較適合用於 domain() 。簡言之:我們有兩組衝突的路由,因此至少其中一組必須綁定到特定網域。由於租戶網域是動態的,這對它們來說不可行,所以我們將此作法用於中央路由。

One more tip here is to only have one central domain. The reason for that is that you cannot use route names if you have multiple domains. Route names are unique, but each route definition bound to a domain is a separate route. Therefore, the last registered route with the name would be the only one to get actually registered with that name.
這裡還有一個小建議:中央網域最好只設定一個。原因是如果有多個網域,就無法使用路由名稱。路由名稱是唯一的,但每個綁定到網域的路由定義都是獨立的路由。因此,最後註冊的同名路由將是唯一實際以該名稱註冊的路由。

Route registration order
路由註冊順序

It’s important that your tenant routes are registered after central routes. The reason for this is that routes registered earlier take precedence over routes registered later.
租戶路由必須在中央路由之後註冊,這點非常重要。原因是先註冊的路由會優先於後註冊的路由。

You can observe a similar effect when registering routes like this:
你可以觀察到類似的效果,當你這樣註冊路由時:

Route::get('/products/edit', ...);
Route::get('/products/{product}', ...);

The edit route needs to be registered first. If the routes were registered in the opposite order, Laravel’s routing would try to fit edit into {product} instead of realizing it’s a separate route. You’re probably familiar with this if you’ve built a CRUD app with Laravel before.
編輯路由需要先註冊。如果路由註冊的順序相反,Laravel 的路由系統會嘗試將 edit 配對到 {product} ,而不是意識到它是另一條獨立的路由。如果你之前用 Laravel 建立過 CRUD 應用,應該對此很熟悉。

In the context of multi-tenancy, it’s similar. If our tenant routes — which include a / route and only restrict access from central domains using middleware — were registered first, Laravel wouldn’t even look for the central / route since it already found a route that matches /.
在多租戶的情境下也是類似的。如果我們的租戶路由(包含一條 / 路由,並且只用中介軟體限制中央網域的存取)先被註冊,Laravel 就不會去尋找中央的 / 路由,因為它已經找到一條符合 / 的路由了。

If we register the central route first (the proper approach), Laravel will:
如果我們先註冊中央路由(正確的做法),Laravel 將會:

  • notice the route first, and use it on central domains after verifying that the request domain matches the route domain
    先注意到該路由,並在確認請求的網域與路由網域相符後,於中央網域使用該路由
  • ignore the route on tenant requests, since the domain() condition doesn’t match
    在租戶請求時忽略該路由,因為 domain() 條件不符合

Alternative way of registering routes
註冊路由的替代方法

In Getting started we mentioned that there are two different ways to register your routes. One involves wrapping your central routes in a domain() scope.
在「快速入門」中,我們提到有兩種不同的方式來註冊路由。其中一種是將您的中央路由包裹在 domain() 範圍內。

The other solution — discussed here — lets you keep a clean structure in web.php, but requires a bit more configuration and has some minor drawbacks, which is why it’s not the solution we suggest in the Getting started guide. That said, you may find this approach to be a lot more convenient, especially as your application grows.
另一種解決方案 — 在此討論 — 讓您能在 web.php 中保持乾淨的結構,但需要更多的設定,且有一些小缺點,這也是為什麼我們在「快速入門」指南中不建議使用此方案。儘管如此,您可能會發現這種方法更為方便,尤其是當您的應用程式規模擴大時。

In essence, this solution registers routes via the using parameter of withRouting() method in your bootstrap/app.php file:
本質上,這個解決方案是透過在您的 bootstrap/app.php 檔案中,使用 withRouting() 方法的 using 參數來註冊路由:

bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
using: function () {
$centralDomains = config('tenancy.identification.central_domains');
foreach ($centralDomains as $domain) {
Route::middleware('web')
->domain($domain)
->group(base_path('routes/web.php'));
}
Route::middleware('tenant')->group(base_path('routes/tenant.php'));
},
)

You can see that we are registering both the central and tenant routes in this closure. This means all of your route registration happens in one place.
你可以看到我們在這個閉包中同時註冊了中央和租戶的路由。這表示你所有的路由註冊都集中在一個地方進行。

Since we’re registering the tenant routes here, let’s disable route registration in the TenancyServiceProvider:
既然我們在這裡註冊租戶路由,讓我們在 TenancyServiceProvider 中停用路由註冊:

app/Providers/TenancyServiceProvider.php
public function boot()
{
$this->bootEvents();
// $this->mapRoutes();

The only drawback here is that the healthcheck route doesn’t get registered, so if you’d like to use it, you’d need to manually register it as part of the using closure.
這裡唯一的缺點是 healthcheck 路由不會被註冊,因此如果你想使用它,就需要手動將它註冊為 using 閉包的一部分。

Common patterns  常見模式

In large applications, you may want to separate your tenant routes into web routes and API routes:
在大型應用程式中,您可能會想將租戶路由分為網頁路由和 API 路由:

bootstrap/app.php
Route::middleware('tenant')->group(base_path('routes/tenant.php'));
Route::middleware(['tenant', 'web'])->group(base_path('routes/tenant/web.php'));
Route::middleware(['tenant', 'api'])->group(base_path('routes/tenant/api.php'));

You may also want to automatically apply all the necessary middleware, so that you can keep your tenant route files clean:

bootstrap/app.php
Route::middleware([
'tenant',
InitializeTenancyByDomain::class,
PreventAccessFromUnwantedDomains::class
])->group(function () {
Route::middleware('web')->group(base_path('routes/tenant/web.php'));
Route::middleware('api')->group(base_path('routes/tenant/api.php'));
});

Remember that you don’t have to use the routes/tenant.php file — that’s just the default file registered by TenancyServiceProvider.

You can use the tenancy middleware in any route file — except files like web.php if they’re scoped to the central domain.