如何建立自訂路由載入器
基本應用程式可以在單一設定檔中定義所有路由 - 通常是 config/routes.yaml
(請參閱 路由)。然而,在大多數應用程式中,從不同資源匯入路由定義是很常見的:控制器檔案中的 PHP 屬性、YAML、XML 或 PHP 檔案儲存在某些目錄等。
內建路由載入器
Symfony 為最常見的需求提供了幾個路由載入器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# config/routes.yaml
app_file:
# loads routes from the given routing file stored in some bundle
resource: '@AcmeBundle/Resources/config/routing.yaml'
app_psr4:
# loads routes from the PHP attributes of the controllers found in the given PSR-4 namespace root
resource:
path: '../src/Controller/'
namespace: App\Controller
type: attribute
app_attributes:
# loads routes from the PHP attributes of the controllers found in that directory
resource: '../src/Controller/'
type: attribute
app_class_attributes:
# loads routes from the PHP attributes of the given class
resource: App\Controller\MyController
type: attribute
app_directory:
# loads routes from the YAML, XML or PHP files found in that directory
resource: '../legacy/routing/'
type: directory
app_bundle:
# loads routes from the YAML, XML or PHP files found in some bundle directory
resource: '@AcmeOtherBundle/Resources/config/routing/'
type: directory
注意
匯入資源時,鍵 (例如 app_file
) 是集合的名稱。只需確保它在每個檔案中都是唯一的,這樣沒有其他行會覆蓋它。
如果您的應用程式需求不同,您可以建立自己的自訂路由載入器,如下一節所述。
什麼是自訂路由載入器
自訂路由載入器使您能夠根據某些慣例、模式或整合來產生路由。此用例的一個範例是 OpenAPI-Symfony-Routing 程式庫,其中路由是根據 OpenAPI/Swagger 屬性產生的。另一個範例是 SonataAdminBundle,它根據 CRUD 慣例建立路由。
載入路由
Symfony 應用程式中的路由由 DelegatingLoader 載入。此載入器使用其他幾個載入器 (委派) 來載入不同類型的資源,例如 YAML 檔案或控制器檔案中的 #[Route]
屬性。專業的載入器實作 LoaderInterface,因此具有兩個重要的方法:supports() 和 load()。
以下程式碼取自 routes.yaml
1 2 3 4
# config/routes.yaml
controllers:
resource: ../src/Controller/
type: attribute
當主載入器解析此程式碼時,它會嘗試所有已註冊的委派載入器,並使用給定的資源 (../src/Controller/
) 和類型 (attribute
) 作為引數來呼叫其 supports() 方法。當其中一個載入器傳回 true
時,將會呼叫其 load() 方法,該方法應傳回包含 RouteCollection 的 Route 物件。
注意
以這種方式載入的路由將由路由器快取,與在預設格式 (例如 XML、YAML、PHP 檔案) 中定義時的方式相同。
使用自訂服務載入路由
使用常規 Symfony 服務是以自訂方式載入路由的最簡單方法。這比建立完整的自訂路由載入器容易得多,因此您應該始終首先考慮此選項。
若要執行此操作,請定義 type: service
作為載入的路由資源類型,並設定要呼叫的服務和方法
1 2 3 4
# config/routes.yaml
admin_routes:
resource: 'admin_route_loader::loadRoutes'
type: service
在此範例中,路由是透過呼叫 ID 為 admin_route_loader
的服務的 loadRoutes()
方法來載入的。您的服務不必擴充或實作任何特殊類別,但呼叫的方法必須傳回 RouteCollection 物件。
如果您使用 autoconfigure,您的類別應實作 RouteLoaderInterface 介面以自動標記。如果您未使用 autoconfigure,請使用 routing.route_loader
手動標記它。
注意
使用服務路由載入器定義的路由將由框架自動快取。因此,每當您的服務應載入新路由時,請不要忘記清除快取。
提示
如果您的服務是可調用的,則無需指定要使用的方法。
建立自訂載入器
若要從某些自訂來源 (即從屬性、YAML 或 XML 檔案以外的來源) 載入路由,您需要建立自訂路由載入器。此載入器必須實作 LoaderInterface。
在大多數情況下,從 Loader 擴充比自己實作 LoaderInterface 更容易。
下面的範例載入器支援載入類型為 extra
的路由資源。類型名稱不應與可能支援相同資源類型的其他載入器衝突。設定任何特定於您所做事情的名稱。範例中實際上未使用資源名稱本身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
// src/Routing/ExtraLoader.php
namespace App\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class ExtraLoader extends Loader
{
private bool $isLoaded = false;
public function load($resource, ?string $type = null): RouteCollection
{
if (true === $this->isLoaded) {
throw new \RuntimeException('Do not add the "extra" loader twice');
}
$routes = new RouteCollection();
// prepare a new route
$path = '/extra/{parameter}';
$defaults = [
'_controller' => 'App\Controller\ExtraController::extra',
];
$requirements = [
'parameter' => '\d+',
];
$route = new Route($path, $defaults, $requirements);
// add the new route to the route collection
$routeName = 'extraRoute';
$routes->add($routeName, $route);
$this->isLoaded = true;
return $routes;
}
public function supports($resource, ?string $type = null): bool
{
return 'extra' === $type;
}
}
請確保您指定的控制器確實存在。在這種情況下,您必須在 ExtraController
中建立 extra()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Controller/ExtraController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class ExtraController extends AbstractController
{
public function extra(mixed $parameter): Response
{
return new Response($parameter);
}
}
現在為 ExtraLoader
定義服務
1 2 3 4 5 6
# config/services.yaml
services:
# ...
App\Routing\ExtraLoader:
tags: [routing.loader]
請注意標籤 routing.loader
。具有此標籤的所有服務都將標記為潛在的路由載入器,並作為專門的路由載入器新增到 routing.loader
服務,它是 DelegatingLoader 的實例。
使用自訂載入器
如果您沒有做其他任何事情,則不會呼叫您的自訂路由載入器。剩下要做的是在路由設定中新增幾行
1 2 3 4
# config/routes.yaml
app_extra:
resource: .
type: extra
這裡的重要部分是 type
鍵。其值應為 extra
,因為這是 ExtraLoader
支援的類型,這將確保呼叫其 load()
方法。resource
鍵對於 ExtraLoader
而言並不重要,因此將其設定為 .
(單個點)。
注意
使用自訂路由載入器定義的路由將由框架自動快取。因此,每當您在載入器類別本身中變更某些內容時,請不要忘記清除快取。
更進階的載入器
如果您的自訂路由載入器從 Loader 擴充,如上所示,您也可以利用提供的解析器,即 LoaderResolver 的實例,來載入次要路由資源。
您仍然需要實作 supports() 和 load()。每當您想要載入另一個資源 (例如 YAML 路由設定檔) 時,您可以呼叫 import() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// src/Routing/AdvancedLoader.php
namespace App\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
class AdvancedLoader extends Loader
{
public function load($resource, ?string $type = null): RouteCollection
{
$routes = new RouteCollection();
$resource = '@ThirdPartyBundle/Resources/config/routes.yaml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$routes->addCollection($importedRoutes);
return $routes;
}
public function supports($resource, ?string $type = null): bool
{
return 'advanced_extra' === $type;
}
}
注意
匯入的路由設定的資源名稱和類型可以是路由設定載入器通常支援的任何內容 (YAML、XML、PHP、屬性等)。
注意
如需更進階的用途,請查看 Symfony CMF 專案提供的 ChainRouter。此路由器允許應用程式使用兩個或多個路由器組合,例如在編寫自訂路由器時繼續使用預設的 Symfony 路由系統。