什么是 Symfony 路由组件?
Symfony 路由组件是一个非常流行的路由组件,它由多个框架改编而成,如果您希望在 PHP 应用程序中设置路由,它可以提供很大的灵活性。
如果您已经构建了自定义 PHP 应用程序并正在寻找功能丰富的路由库,那么 Symfony 路由组件是最佳候选之一。它还允许您以 YAML 格式定义应用程序的路由。
从安装和配置开始,我们将通过实际示例来演示该组件用于路由配置的各种选项。在本文中,您将了解到:
- Symfony 路由组件的安装和配置
- 如何设置基本路线
- 如何从 YAML 文件加载路由
- 将路由创建为注释:推荐方式
安装和配置
在本节中,我们将安装在 PHP 应用程序中设置路由所需的库。我假设您已经在系统中安装了 Composer,因为我们需要它来安装 Packagist 上提供的必要库。
安装 Composer 后,请继续使用以下命令安装核心路由组件。
'
$composer require symfony/routing
虽然路由组件本身足以在您的应用程序中提供全面的路由功能,但我们还将继续安装一些其他组件,以使我们的生活更轻松并丰富现有的核心路由功能。
首先,我们将继续安装 HttpFoundation 组件,该组件为 PHP 全局变量和响应相关函数提供面向对象的包装器。它确保您不需要直接访问 $_GET
、$_POST
等全局变量。
$composer require symfony/http-foundation
接下来,如果您想在 YAML 文件而不是 PHP 代码中定义应用程序路由,则 YAML 组件可以发挥作用,因为它可以帮助您将 YAML 字符串转换为 PHP 数组,反之亦然。
'
$composer require symfony/yaml
最后,我们将安装 Config 组件,它提供了几个实用程序类来初始化和处理不同类型文件(如 YAML、INI、XML 等)中定义的配置值。在我们的例子中,我们将使用它从 YAML 文件加载路由。
'
$composer require symfony/config
这就是安装部分,但是你应该如何使用它呢?事实上,只需将 Composer 创建的 autoload.php 文件包含在您的应用程序中即可,如以下代码片段所示。
'
<?php
require_once './vendor/autoload.php';
// application code
?>
设置基本路由
在上一节中,我们完成了必要的路由组件的安装。现在,您可以立即在 PHP 应用程序中设置路由。
让我们继续创建包含以下内容的 basic_routes.php 文件。
<?php
require_once './vendor/autoload.php';
use SymfonyComponentRoutingMatcherUrlMatcher;
use SymfonyComponentRoutingRequestContext;
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingGeneratorUrlGenerator;
use SymfonyComponentRoutingExceptionResourceNotFoundException;
try
{
// Init basic route
$foo_route = new Route(
'/foo',
array('controller' => 'FooController')
);
// Init route with dynamic placeholders
$foo_placeholder_route = new Route(
'/foo/{id}',
array('controller' => 'FooController', 'method'=>'load'),
array('id' => '[0-9]+')
);
// Add Route object(s) to RouteCollection object
$routes = new RouteCollection();
$routes->add('foo_route', $foo_route);
$routes->add('foo_placeholder_route', $foo_placeholder_route);
// Init RequestContext object
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());
// Init UrlMatcher object
$matcher = new UrlMatcher($routes, $context);
// Find the current route
$parameters = $matcher->match($context->getPathInfo());
// How to generate a SEO URL
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate('foo_placeholder_route', array(
'id' => 123,
));
echo ''
; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
使用 Symfony Routing 组件设置路由通常要经历如下列出的一系列步骤。
- 为每个应用程序路由初始化
Route
对象。 - 将所有
Route
对象添加到RouteCollection
对象。 - 初始化
RequestContext
对象,该对象保存当前请求上下文信息。 - 通过传递
RouteCollection
对象和RequestContext
对象来初始化UrlMatcher
对象。
初始化不同路由的路由对象
让我们继续定义一个非常基本的 foo
路由。
$foo_route = new Route(
'/foo',
array('controller' => 'FooController')
);
Route
构造函数的第一个参数是 URI 路径,第二个参数是匹配此特定路由时要返回的自定义属性数组。通常,它是控制器和方法的组合,当请求此路由时您要调用它们。
接下来我们看一下参数化路由。
'
$foo_placeholder_route = new Route(
'/foo/{id}',
array('controller' => 'FooController', 'method'=>'load'),
array('id' => '[0-9]+')
);
上述路由可以匹配 foo/1
、foo/123
等类似的 URI。请注意,我们将 {id}
参数限制为仅限数字值,因此它不会匹配 foo/bar
这样的 URI,因为 {id}
参数以字符串形式提供。
将所有路由对象添加到 RouteCollection 对象
下一步是将我们在上一节中初始化的路由对象添加到 RouteCollection
对象。
$routes = new RouteCollection();
$routes->add('foo_route', $foo_route);
$routes->add('foo_placeholder_route', $foo_placeholder_route);
正如您所看到的,这非常简单,您只需要使用 RouteCollection
对象的 add
方法来添加路由对象。 add
方法的第一个参数是路由名称,第二个参数是路由对象本身。
初始化 RequestContext
对象
接下来,我们需要初始化RequestContext
对象,该对象保存当前请求上下文信息。当我们初始化 UrlMatcher
对象时,我们将需要这个对象,因为我们稍后会详细介绍它。
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());
初始化 UrlMatcher
对象
最后,我们需要初始化 UrlMatcher
对象以及路由和上下文信息。
// Init UrlMatcher object
$matcher = new UrlMatcher($routes, $context);
现在,我们拥有了可以匹配路线的一切。
如何匹配路由
这是 UrlMatcher
对象的 match
方法,它允许您将任何路由与一组预定义路由进行匹配。
match
方法将 URI 作为其第一个参数,并尝试将其与预定义的路由进行匹配。如果找到该路由,它将返回与该路由关联的自定义属性。另一方面,如果没有与当前 URI 关联的路由,它会抛出 ResourceNotFoundException
异常。
$parameters = $matcher->match($context->getPathInfo());
在我们的例子中,我们通过从 $context
对象获取当前 URI 来提供它。因此,如果您访问 https://your-domain/basic_routes.php/foo URL,则 $context->getPathInfo()
返回 foo
,并且我们已经为 foo
URI 定义了一条路由,因此它应该返回以下内容。
Array
(
[controller] => FooController
[_route] => foo_route
)
现在,让我们继续访问 http://your-domain/basic_routes.php/foo/123 URL 来测试参数化路由。
'
Array
(
[controller] => FooController
[method] => load
[id] => 123
[_route] => foo_placeholder_route
)
如果您可以看到 id
参数与适当的值 123
绑定,则说明有效。
接下来,让我们尝试访问不存在的路由,例如 http://your-domain/basic_routes.php/unknown-route,您应该会看到以下消息。
'
No routes found for "/unknown-route".
这就是如何使用 match
方法查找路由。
除此之外,您还可以使用路由组件在应用程序中生成链接。提供了 RouteCollection
和 RequestContext
对象,UrlGenerator
允许您为特定路由构建链接。
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate('foo_placeholder_route', array(
'id' => 123,
));
generate
方法的第一个参数是路由名称,第二个参数是数组,如果是参数化路由,则可以包含参数。上面的代码应该生成 /basic_routes.php/foo/123 URL。
从 YAML 文件加载路由
在上一节中,我们使用 Route
和 RouteCollection
对象构建了自定义路由。事实上,路由组件提供了不同的方式供您选择来实例化路由。您可以从各种加载器中进行选择,例如 YamlFileLoader
、XmlFileLoader
和 PhpFileLoader
。
在本节中,我们将通过 YamlFileLoader
加载器来了解如何从 YAML 文件加载路由。
路由 YAML 文件
继续创建包含以下内容的 routes.yaml 文件。
'
foo_route:
path: /foo
controller: AppControllerFooController::index
methods: GET
foo_placeholder_route:
path: /foo/{id}
controller: AppControllerFooController::load
methods: GET
requirements:
id: '[0-9]+'
示例文件
接下来,继续使用以下内容创建 load_routes_from_yaml.php 文件。
'
<?php
require_once './vendor/autoload.php';
use SymfonyComponentRoutingMatcherUrlMatcher;
use SymfonyComponentRoutingRequestContext;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingGeneratorUrlGenerator;
use SymfonyComponentConfigFileLocator;
use SymfonyComponentRoutingLoaderYamlFileLoader;
use SymfonyComponentRoutingExceptionResourceNotFoundException;
try
{
// Load routes from the yaml file
$fileLocator = new FileLocator(array(__DIR__));
$loader = new YamlFileLoader($fileLocator);
$routes = $loader->load('routes.yaml');
// Init RequestContext object
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());
// Init UrlMatcher object
$matcher = new UrlMatcher($routes, $context);
// Find the current route
$parameters = $matcher->match($context->getPathInfo());
// How to generate a SEO URL
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate('foo_placeholder_route', array(
'id' => 123,
));
echo ''
'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
在这种情况下唯一不同的是我们初始化路由的方式!
'
$fileLocator = new FileLocator(array(__DIR__));
$loader = new YamlFileLoader($fileLocator);
$routes = $loader->load('routes.yaml');
我们使用 YamlFileLoader
加载器从 routes.yaml 文件加载路由,而不是直接在 PHP 本身中对其进行初始化。除此之外,一切都是相同的,并且应该产生与 basic_routes.php 文件相同的结果。
一体化路由器
在本节中,我们将介绍 Router
类,它允许您使用更少的代码行快速设置路由。
继续制作包含以下内容的 all_in_one_router.php 文件。
'
<?php
require_once './vendor/autoload.php';
use SymfonyComponentRoutingRequestContext;
use SymfonyComponentRoutingRouter;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingGeneratorUrlGenerator;
use SymfonyComponentConfigFileLocator;
use SymfonyComponentRoutingLoaderYamlFileLoader;
use SymfonyComponentRoutingExceptionResourceNotFoundException;
try
{
$fileLocator = new FileLocator(array(__DIR__));
$requestContext = new RequestContext();
$requestContext->fromRequest(Request::createFromGlobals());
$router = new Router(
new YamlFileLoader($fileLocator),
'routes.yaml',
array('cache_dir' => __DIR__.'/cache'),
$requestContext
);
// Find the current route
$parameters = $router->match($requestContext->getPathInfo());
// How to generate a SEO URL
$routes = $router->getRouteCollection();
$generator = new UrlGenerator($routes, $requestContext);
$url = $generator->generate('foo_placeholder_route', array(
'id' => 123,
));
echo ''
'; print_r($parameters); echo 'Generated URL: ' . $url; exit; } catch (ResourceNotFoundException $e) { echo $e->getMessage(); }
一切都几乎相同,除了我们实例化了 Router
对象以及必要的依赖项。
$router = new Router(
new YamlFileLoader($fileLocator),
'routes.yaml',
array('cache_dir' => __DIR__.'/cache'),
$requestContext
);
完成后,您可以立即使用 Router 对象的 match
方法进行路由映射。
$parameters = $router->match($requestContext->getPathInfo());
此外,您还需要使用 Router 对象的 getRouteCollection
方法来获取路由。
$routes = $router->getRouteCollection();
将路由创建为注释:推荐方式
在本节中,我们将讨论如何实现基于注释的路由。它正在成为在不同框架之间定义路由的最流行的方法之一。
在我们继续实现基于注释的路由之前,我们需要安装几个软件包。让我们快速完成此操作,如以下代码片段所示。
'
$composer require symfony/framework-bundle
$composer require doctrine/annotations
$composer require doctrine/cache
如您所见,我们安装了三个不同的组件。
在您的 composer.json 文件中,添加以下内容:
'
"autoload": {
"psr-4": {
"App": "app/"
}
}
现在,运行以下命令。
'
$composer dump-autoload
现在,我们准备好文件了。
继续创建包含以下内容的 index.php 文件。
'
<?php
use DoctrineCommonAnnotationsAnnotationReader;
use SymfonyBundleFrameworkBundleRoutingAnnotatedRouteControllerLoader;
use SymfonyComponentConfigFileLocator;
use SymfonyComponentRoutingLoaderAnnotationDirectoryLoader;
use ComposerAutoloadClassLoader;
use DoctrineCommonAnnotationsAnnotationRegistry;
use SymfonyComponentRoutingRequestContext;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingMatcherUrlMatcher;
/** @var ClassLoader $loader */
$loader = require __DIR__ . '/vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$loader = new AnnotationDirectoryLoader(
new FileLocator(__DIR__ . '/src/Controller/'),
new AnnotatedRouteControllerLoader(
new AnnotationReader()
)
);
$routes = $loader->load(__DIR__ . '/src/Controller/');
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->match($context->getPathInfo());
$controllerInfo = explode('::',$parameters['_controller']);
$controller = new $controllerInfo[0];
$action = $controllerInfo[1];
$controller->$action();
现在,让我们在 src/Controller/FooController.php 中创建包含以下内容的控制器文件。
'
<?php
namespace AppController;
use SymfonyComponentRoutingAnnotationRoute;
class DefaultController
{
/**
* @Route("/",name="index")
*/
public function index()
{
echo "Index action";
}
/**
* @Route("/hello",name="hello")
*/
public function hello()
{
echo "Hello action";
}
}
您可能已经注意到,我们以注释的形式为每个方法定义了路由。这种方法的好处是,它允许您在与这些路由关联的控制器的代码旁边定义路由。
继续访问 https://your-domain/index.php/ URL。根据以下路由配置,它应该调用 index
方法。
/**
* @Route("/",name="index")
*/
另一方面,如果您尝试访问 http://your-domain/index.php/hello URL,它应该调用 DefaultController
控制器的 hello
方法类。
这就是基于注释的路由的工作原理!
结论
继续探索路由组件中可用的其他选项。
今天,我们探索了 Symfony 路由组件,它使得在 PHP 应用程序中实现路由变得轻而易举。在此过程中,我们创建了一些示例来演示路由组件的各个方面。