步骤 6: 创建控制器
当一个 HTTP 请求进来的时候,比如访问首页的时候(),Symfony 试着去找到一条匹配 请求路径 的 路由 (在本例中是请求路径 /
)。一条 路由 就是一个请求路径和一个 PHP callable 之间的链接,而这个 PHP callable 是一个函数,它为该请求生成一个 HTTP 应答。
这些 callable 称为“控制器”。在 Symfony 里,大部分控制器都以 PHP 类的形式来实现。你可以手工创建这样的一个类,但是因为我们喜欢快速开发,我们来看看 Symfony 如何能帮助我们。
我们可以用 symfony/maker-bundle
包来轻松生成控制器。
因为 maker bundle 只是在开发时用到,不要忘记加上``–dev``选项,避免在生产环境中启用它。
maker bundle 帮你生成很多不同的类。我们在本书中会一直用到它。每一个”生成器“对应一个命令,所有这些命令都是 make
命令命名空间的一部分。
Symfony Console 自带 list
命令,它会列出某个命名空间下的所有可用命令;你用它来查看 maker bundle 提供的所有生成器。
$ symfony console list make
在创建项目的第一个控制器之前,我们需要选择配置的格式。Symfony 自带支持YAML、XML、PHP 和注解(annotation)。
对于 和依赖包相关的配置,YAML 是最好的选择。这是 config/
目录里的文件所使用的格式。一种常见的情况是,当你安装一个新的包时,包的 recipe 会添加一个 .yaml
后缀的文件到 config/
目录下。
对于 和 PHP 代码相关的配置,注解 是更好的选择,因为它们就定义在代码旁边。让我用一个例子来解释下。当一个请求进来,需要有一些配置去告诉 Symfony 该请求路径需要被哪个具体的控制器(一个 PHP 类)来处理。当使用 YAML、XML 或者 PHP 这些配置格式的时候,涉及到两个文件(配置文件和 PHP 控制器文件)。当使用注解时,配置是直接写在控制器类里的。
我们需要添加另一个依赖包来管理注解。
$ symfony composer req annotations
用 make:controller
命令来创建你的第一个 控制器:
这个命令在 src/Controller/
目录下新建了一个名为 ConferenceController
的类。这个生成的类由一些样本代码组成,你可以去微调它。
src/Controller/ConferenceController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
#[Route('/conference', name: 'conference')]
public function index(): Response
{
return $this->render('conference/index.html.twig', [
'controller_name' => 'ConferenceController',
]);
}
}
#[Route('/conference', name: 'conference')]
注解使 index()
方法成为一个控制器(这段配置就在它所配置的代码旁边)。
当你用浏览器访问 /conference
路径时,这个控制器就会被执行,它返回一个应答。
调整一下路由,让它匹配首页的路径:
patch_file
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
class ConferenceController extends AbstractController
{
+ #[Route('/', name: 'homepage')]
public function index(): Response
{
return $this->render('conference/index.html.twig', [
当我们想要在代码里引用首页的地址时,路由的 name
属性会被用到。我们不会硬编码 /
路径,而是会用路由名。
我们来返回一个简单的 HTML 页面,来取代这个默认的页面。
patch_file
控制器的主要责任就是针对请求返回一个 HTTP Response
对象。
为了演示应答如何来利用请求中的信息,让我们来加一个小的 彩蛋#In_computing)。当首页路径包含了一个类似 ?hello=Fabien
的查询字符串时,让我们添加一些文字来对字符串中的人致意。
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,6 +3,7 @@
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
@@ -11,11 +12,17 @@ class ConferenceController extends AbstractController
#[Route('/', name: 'homepage')]
- public function index(): Response
+ public function index(Request $request): Response
{
+ $greet = '';
+ if ($name = $request->query->get('hello')) {
+ $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+ }
+
return new Response(<<<EOF
<html>
<body>
</body>
</html>
Symfony 通过 Request
对象来暴露请求中的数据。当 Symfony 看到控制器里有 Request
类型的参数,它会被传递给你。我们可以用它来获得查询字符串中的名字,然后添加一个 <h1>
标题。
现在浏览器里打开 /
路径,再打开 /?hello=Fabien
路径,看一下两者的不同。
注解
注意一下对 htmlspecialchars()
函数的调用,是为了防止 XSS 攻击。当我们切换到一个良好的模板引擎后,这些都会自动为我们完成。
我们也可以把这个人的名字作为 URL 的一部分:
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -9,13 +9,19 @@ use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
- #[Route('/', name: 'homepage')]
+ #[Route('/hello/{name}', name: 'homepage')]
- public function index(): Response
+ public function index(string $name = ''): Response
{
+ $greet = '';
+ if ($name) {
+ $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+ }
+
return new Response(<<<EOF
<html>
<body>
+ $greet
<img src="/images/under-construction.gif" />
</body>
</html>
路由中的 {name}
部分是动态 路由参数,它类似于通配符。你可以在浏览器先访问 /hello
再访问 /hello/Fabien
,看到的结果和之前一样。在控制器参数里用同 名 的参数,能得到 {name}
参数的 值。在本例中,参数名就叫 。
深入学习
- Symfony 系统;
- SymfonyCasts 的路由,控制器和页面教程;
- PHP 中的 ;
- HttpFoundation 组件;
- 。