控制器
控制器是一个您创建的 PHP 函数,它从 Request
对象读取信息,并创建和返回一个 Response
对象。响应可以是 HTML 页面、JSON、XML、文件下载、重定向、404 错误或任何其他内容。控制器运行您的应用程序需要渲染页面内容的任何任意逻辑。
提示
如果您尚未创建您的第一个工作页面,请查看 在 Symfony 中创建您的第一个页面,然后再回来!
一个基本的控制器
虽然控制器可以是任何 PHP 可调用对象(函数、对象方法或 Closure
),但控制器通常是控制器类中的一个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Controller/LuckyController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class LuckyController
{
#[Route('/lucky/number/{max}', name: 'app_lucky_number')]
public function number(int $max): Response
{
$number = random_int(0, $max);
return new Response(
'<html><body>Lucky number: '.$number.'</body></html>'
);
}
}
控制器是 number()
方法,它位于控制器类 LuckyController
中。
这个控制器非常简单
- 第 2 行:Symfony 利用 PHP 的命名空间功能来命名整个控制器类。
- 第 4 行:Symfony 再次利用 PHP 的命名空间功能:
use
关键字导入Response
类,控制器必须返回该类。 - 第 7 行:该类在技术上可以被命名为任何名称,但按照惯例,它以
Controller
为后缀。 - 第 10 行:由于 路由中的通配符
{max}
,操作方法允许有一个$max
参数。 - 第 14 行:控制器创建并返回一个
Response
对象。
将 URL 映射到控制器
为了查看此控制器的结果,您需要通过路由将 URL 映射到它。这在上面通过 路由属性 #[Route('/lucky/number/{max}')]
完成。
要查看您的页面,请在浏览器中转到此 URL:http://127.0.0.1:8000/lucky/number/100
有关路由的更多信息,请参阅 路由。
基础控制器类 & 服务
为了辅助开发,Symfony 提供了一个可选的基础控制器类,名为 AbstractController。它可以被扩展以访问辅助方法。
在您的控制器类的顶部添加 use
语句,然后修改 LuckyController
以扩展它
1 2 3 4 5 6 7 8 9 10
// src/Controller/LuckyController.php
namespace App\Controller;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
- class LuckyController
+ class LuckyController extends AbstractController
{
// ...
}
就是这样!您现在可以访问诸如 $this->render() 等方法以及您将在接下来了解的许多其他方法。
生成 URL
generateUrl() 方法只是一个辅助方法,用于生成给定路由的 URL
1
$url = $this->generateUrl('app_lucky_number', ['max' => 10]);
重定向
如果您想将用户重定向到另一个页面,请使用 redirectToRoute()
和 redirect()
方法
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
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
// ...
public function index(): RedirectResponse
{
// redirects to the "homepage" route
return $this->redirectToRoute('homepage');
// redirectToRoute is a shortcut for:
// return new RedirectResponse($this->generateUrl('homepage'));
// does a permanent HTTP 301 redirect
return $this->redirectToRoute('homepage', [], 301);
// if you prefer, you can use PHP constants instead of hardcoded numbers
return $this->redirectToRoute('homepage', [], Response::HTTP_MOVED_PERMANENTLY);
// redirect to a route with parameters
return $this->redirectToRoute('app_lucky_number', ['max' => 10]);
// redirects to a route and maintains the original query string parameters
return $this->redirectToRoute('blog_show', $request->query->all());
// redirects to the current route (e.g. for Post/Redirect/Get pattern):
return $this->redirectToRoute($request->attributes->get('_route'));
// redirects externally
return $this->redirect('http://symfony.ac.cn/doc');
}
危险
redirect()
方法不会以任何方式检查其目标。如果您重定向到最终用户提供的 URL,您的应用程序可能会受到未验证的重定向安全漏洞的攻击。
渲染模板
如果您正在提供 HTML,您将需要渲染一个模板。render()
方法渲染一个模板,并将该内容放入一个 Response
对象中供您使用
1 2
// renders templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);
模板和 Twig 在 创建和使用模板文章中有更详细的解释。
获取服务
Symfony 打包了很多有用的类和功能,称为 服务。这些用于渲染模板、发送电子邮件、查询数据库以及您可以想到的任何其他“工作”。
如果您需要在控制器中使用服务,请使用其类(或接口)名称类型提示一个参数,Symfony 将自动注入它。这需要您的控制器注册为服务
1 2 3 4 5 6 7 8 9 10
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
// ...
#[Route('/lucky/number/{max}')]
public function number(int $max, LoggerInterface $logger): Response
{
$logger->info('We are logging!');
// ...
}
太棒了!
您可以使用类型提示哪些其他服务?要查看它们,请使用 debug:autowiring
控制台命令
1
$ php bin/console debug:autowiring
提示
如果您需要控制参数确切的值,或者需要参数,您可以使用 #[Autowire]
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ...
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
class LuckyController extends AbstractController
{
public function number(
int $max,
// inject a specific logger service
#[Autowire(service: 'monolog.logger.request')]
LoggerInterface $logger,
// or inject parameter values
#[Autowire('%kernel.project_dir%')]
string $projectDir
): Response
{
$logger->info('We are logging!');
// ...
}
}
您可以在 自动定义服务依赖项(自动装配)中阅读有关此属性的更多信息。
与所有服务一样,您也可以在控制器中使用常规的构造函数注入。
有关服务的更多信息,请参阅 服务容器 文章。
生成控制器
为了节省时间,您可以安装 Symfony Maker 并告诉 Symfony 生成一个新的控制器类
1 2 3 4
$ php bin/console make:controller BrandNewController
created: src/Controller/BrandNewController.php
created: templates/brandnew/index.html.twig
如果您想从 Doctrine 实体生成整个 CRUD,请使用
1 2 3 4 5 6 7 8 9 10
$ php bin/console make:crud Product
created: src/Controller/ProductController.php
created: src/Form/ProductType.php
created: templates/product/_delete_form.html.twig
created: templates/product/_form.html.twig
created: templates/product/edit.html.twig
created: templates/product/index.html.twig
created: templates/product/new.html.twig
created: templates/product/show.html.twig
管理错误和 404 页面
当找不到内容时,您应该返回 404 响应。为此,抛出一种特殊的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// ...
public function index(): Response
{
// retrieve the object from database
$product = ...;
if (!$product) {
throw $this->createNotFoundException('The product does not exist');
// the above is just a shortcut for:
// throw new NotFoundHttpException('The product does not exist');
}
return $this->render(/* ... */);
}
createNotFoundException() 方法只是创建特殊的 NotFoundHttpException 对象的快捷方式,它最终会在 Symfony 内部触发 404 HTTP 响应。
如果您抛出一个异常,该异常扩展或是一个 HttpException 的实例,Symfony 将使用适当的 HTTP 状态代码。否则,响应将具有 500 HTTP 状态代码
1 2
// this exception ultimately generates a 500 status error
throw new \Exception('Something went wrong!');
在任何情况下,都会向最终用户显示错误页面,并向开发人员显示完整的调试错误页面(即当您处于“Debug”模式时 - 请参阅 配置 Symfony)。
要自定义向用户显示的错误页面,请参阅 如何自定义错误页面 文章。
Request 对象作为控制器参数
如果您需要读取查询参数、获取请求头或访问上传的文件怎么办?该信息存储在 Symfony 的 Request
对象中。要在您的控制器中访问它,请将其添加为参数并使用 Request 类对其进行类型提示
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...
public function index(Request $request): Response
{
$page = $request->query->get('page', 1);
// ...
}
继续阅读以获取有关使用 Request 对象的更多信息。
请求的自动映射
可以使用属性自动将请求的负载和/或查询参数映射到控制器的操作参数。
单独映射查询参数
假设用户向您发送一个带有以下查询字符串的请求:https://example.com/dashboard?firstName=John&lastName=Smith&age=27
。感谢 MapQueryParameter 属性,控制器的操作参数可以自动实现
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
// ...
public function dashboard(
#[MapQueryParameter] string $firstName,
#[MapQueryParameter] string $lastName,
#[MapQueryParameter] int $age,
): Response
{
// ...
}
#[MapQueryParameter]
可以接受一个名为 filter
的可选参数。您可以使用 PHP 中定义的 Validate Filters 常量
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
// ...
public function dashboard(
#[MapQueryParameter(filter: \FILTER_VALIDATE_REGEXP, options: ['regexp' => '/^\w+$/'])] string $firstName,
#[MapQueryParameter] string $lastName,
#[MapQueryParameter(filter: \FILTER_VALIDATE_INT)] int $age,
): Response
{
// ...
}
映射整个查询字符串
另一种可能性是将整个查询字符串映射到一个对象中,该对象将保存可用的查询参数。假设您声明了以下 DTO 及其可选的验证约束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
namespace App\Model;
use Symfony\Component\Validator\Constraints as Assert;
class UserDto
{
public function __construct(
#[Assert\NotBlank]
public string $firstName,
#[Assert\NotBlank]
public string $lastName,
#[Assert\GreaterThan(18)]
public int $age,
) {
}
}
然后,您可以在控制器中使用 MapQueryString 属性
1 2 3 4 5 6 7 8 9 10 11 12
use App\Model\UserDto;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
// ...
public function dashboard(
#[MapQueryString] UserDto $userDto
): Response
{
// ...
}
您可以自定义映射期间使用的验证组,以及验证失败时要返回的 HTTP 状态
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\HttpFoundation\Response;
// ...
public function dashboard(
#[MapQueryString(
validationGroups: ['strict', 'edit'],
validationFailedStatusCode: Response::HTTP_UNPROCESSABLE_ENTITY
)] UserDto $userDto
): Response
{
// ...
}
如果验证失败,则返回的默认状态代码为 404。
即使请求查询字符串为空,如果您也需要有效的 DTO,请为您的控制器参数设置默认值
1 2 3 4 5 6 7 8 9 10 11 12
use App\Model\UserDto;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
// ...
public function dashboard(
#[MapQueryString] UserDto $userDto = new UserDto()
): Response
{
// ...
}
映射请求负载
在创建 API 并处理除 GET
之外的其他 HTTP 方法(如 POST
或 PUT
)时,用户数据不会存储在查询字符串中,而是直接存储在请求负载中,如下所示
1 2 3 4 5
{
"firstName": "John",
"lastName": "Smith",
"age": 28
}
在这种情况下,也可以通过使用 MapRequestPayload 属性将此负载直接映射到您的 DTO
1 2 3 4 5 6 7 8 9 10 11 12
use App\Model\UserDto;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
// ...
public function dashboard(
#[MapRequestPayload] UserDto $userDto
): Response
{
// ...
}
此属性允许您自定义序列化上下文以及负责在请求和 DTO 之间进行映射的类
1 2 3 4 5 6 7 8 9 10
public function dashboard(
#[MapRequestPayload(
serializationContext: ['...'],
resolver: App\Resolver\UserDtoResolver
)]
UserDto $userDto
): Response
{
// ...
}
您还可以自定义使用的验证组、验证失败时要返回的状态代码以及支持的负载格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Symfony\Component\HttpFoundation\Response;
// ...
public function dashboard(
#[MapRequestPayload(
acceptFormat: 'json',
validationGroups: ['strict', 'read'],
validationFailedStatusCode: Response::HTTP_NOT_FOUND
)] UserDto $userDto
): Response
{
// ...
}
如果验证失败,则返回的默认状态代码为 422。
提示
如果您构建 JSON API,请确保将您的路由声明为使用 JSON 格式。这将使错误处理在发生验证错误时输出 JSON 响应,而不是 HTML 页面
1
#[Route('/dashboard', name: 'dashboard', format: 'json')]
如果您想映射特定 DTO 的嵌套数组,请确保安装 phpstan/phpdoc-parser 和 phpdocumentor/type-resolver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public function dashboard(
#[MapRequestPayload] EmployeesDto $employeesDto
): Response
{
// ...
}
final class EmployeesDto
{
/**
* @param UserDto[] $users
*/
public function __construct(
public readonly array $users = []
) {}
}
您可以告诉 Symfony 将每个 DTO 对象转换为数组并返回如下内容,而不是返回 DTO 对象数组
1 2 3 4 5 6 7 8 9 10 11 12
[
{
"firstName": "John",
"lastName": "Smith",
"age": 28
},
{
"firstName": "Jane",
"lastName": "Doe",
"age": 30
}
]
要做到这一点,请将参数映射为一个数组,并使用属性的 type
选项配置每个元素的类型
1 2 3 4 5 6
public function dashboard(
#[MapRequestPayload(type: UserDto::class)] array $users
): Response
{
// ...
}
7.1
#[MapRequestPayload]
的 type
选项是在 Symfony 7.1 中引入的。
映射上传的文件
Symfony 提供了一个名为 #[MapUploadedFile]
的属性,用于将一个或多个 UploadedFile
对象映射到控制器参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;
use Symfony\Component\Routing\Attribute\Route;
class UserController extends AbstractController
{
#[Route('/user/picture', methods: ['PUT'])]
public function changePicture(
#[MapUploadedFile] UploadedFile $picture,
): Response {
// ...
}
}
在本例中,关联的 参数解析器 根据参数名称($picture
)获取 UploadedFile
。如果未提交文件,则会抛出 HttpException
。您可以通过使控制器参数可为空来更改此行为
1 2
#[MapUploadedFile]
?UploadedFile $document
#[MapUploadedFile]
属性还允许传递要应用于上传文件的约束列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Validator\Constraints as Assert;
class UserController extends AbstractController
{
#[Route('/user/picture', methods: ['PUT'])]
public function changePicture(
#[MapUploadedFile([
new Assert\File(mimeTypes: ['image/png', 'image/jpeg']),
new Assert\Image(maxWidth: 3840, maxHeight: 2160),
])]
UploadedFile $picture,
): Response {
// ...
}
}
验证约束在将 UploadedFile
注入控制器参数之前进行检查。如果存在约束冲突,则会抛出 HttpException
,并且不执行控制器的操作。
如果您需要上传文件集合,请将它们映射到数组或可变参数。给定的约束将应用于所有文件,如果其中任何一个失败,则会抛出 HttpException
1 2 3 4 5
#[MapUploadedFile(new Assert\File(mimeTypes: ['application/pdf']))]
array $documents
#[MapUploadedFile(new Assert\File(mimeTypes: ['application/pdf']))]
UploadedFile ...$documents
使用 name
选项将上传的文件重命名为自定义值
1 2
#[MapUploadedFile(name: 'something-else')]
UploadedFile $document
此外,您可以更改存在约束冲突时抛出的 HTTP 异常的状态代码
1 2 3 4 5
#[MapUploadedFile(
constraints: new Assert\File(maxSize: '2M'),
validationFailedStatusCode: Response::HTTP_REQUEST_ENTITY_TOO_LARGE
)]
UploadedFile $document
7.1
#[MapUploadedFile]
属性是在 Symfony 7.1 中引入的。
管理会话
您可以在用户的会话中存储特殊消息,称为“flash”消息。按照设计,flash 消息旨在仅使用一次:当您检索到它们时,它们会自动从会话中消失。此功能使“flash”消息特别适合存储用户通知。
例如,假设您正在处理 表单 提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...
public function update(Request $request): Response
{
// ...
if ($form->isSubmitted() && $form->isValid()) {
// do some sort of processing
$this->addFlash(
'notice',
'Your changes were saved!'
);
// $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()
return $this->redirectToRoute(/* ... */);
}
return $this->render(/* ... */);
}
阅读以获取有关使用会话的更多信息。
Request 和 Response 对象
正如 前面提到的,Symfony 会将 Request
对象传递给任何使用 Request
类进行类型提示的控制器参数
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
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
public function index(Request $request): Response
{
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(['en', 'fr']);
// retrieves GET and POST variables respectively
$request->query->get('page');
$request->getPayload()->get('page');
// retrieves SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by foo
$request->files->get('foo');
// retrieves a COOKIE value
$request->cookies->get('PHPSESSID');
// retrieves an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content-type');
}
Request
类有几个公共属性和方法,它们返回您需要的关于请求的任何信息。
与 Request
类似,Response
对象也有一个公共的 headers
属性。此对象是 ResponseHeaderBag 类型,并提供了用于获取和设置响应头的方法。header 名称是标准化的。因此,名称 Content-Type
等同于名称 content-type
或 content_type
。
在 Symfony 中,控制器必须返回一个 Response
对象
1 2 3 4 5 6 7 8
use Symfony\Component\HttpFoundation\Response;
// creates a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);
// creates a CSS-response with a 200 status code
$response = new Response('<style> ... </style>');
$response->headers->set('Content-Type', 'text/css');
为了方便这一点,包含了不同的 response 对象来处理不同的响应类型。下面提到了一些。要了解更多关于 Request
和 Response
(以及不同的 Response
类)的信息,请参阅 HttpFoundation 组件文档。
注意
从技术上讲,控制器可以返回 Response
以外的值。但是,您的应用程序负责将该值转换为 Response
对象。这通过 事件(特别是 kernel.view 事件)处理,这是一个您将在稍后学习的高级功能。
访问配置值
要从控制器获取任何 配置参数 的值,请使用 getParameter()
助手方法
1 2 3 4 5 6
// ...
public function index(): Response
{
$contentsDir = $this->getParameter('kernel.project_dir').'/contents';
// ...
}
返回 JSON 响应
要从控制器返回 JSON,请使用 json()
助手方法。这将返回一个 JsonResponse
对象,该对象会自动编码数据
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\HttpFoundation\JsonResponse;
// ...
public function index(): JsonResponse
{
// returns '{"username":"jane.doe"}' and sets the proper Content-Type header
return $this->json(['username' => 'jane.doe']);
// the shortcut defines three optional arguments
// return $this->json($data, $status = 200, $headers = [], $context = []);
}
如果您的应用程序中启用了 serializer 服务,它将用于将数据序列化为 JSON。否则,将使用 json_encode 函数。
流式文件响应
您可以使用 file() 助手从控制器内部提供文件
1 2 3 4 5 6 7 8
use Symfony\Component\HttpFoundation\BinaryFileResponse;
// ...
public function download(): BinaryFileResponse
{
// send the file contents and force the browser to download it
return $this->file('/path/to/some_file.pdf');
}
file()
助手提供了一些参数来配置其行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
// ...
public function download(): BinaryFileResponse
{
// load the file from the filesystem
$file = new File('/path/to/some_file.pdf');
return $this->file($file);
// rename the downloaded file
return $this->file($file, 'custom_name.pdf');
// display the file contents in the browser instead of downloading it
return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}
发送早期提示
Early hints 告诉浏览器在应用程序发送响应内容之前就开始下载一些资源。这提高了感知性能,因为浏览器可以预取一旦最终发送完整响应后将需要的资源。这些资源通常是 Javascript 或 CSS 文件,但它们可以是任何类型的资源。
注意
为了工作,您使用的 SAPI 必须支持此功能,例如 FrankenPHP。
您可以从控制器操作发送 early hints,这要归功于 sendEarlyHints() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\WebLink\Link;
class HomepageController extends AbstractController
{
#[Route("/", name: "homepage")]
public function index(): Response
{
$response = $this->sendEarlyHints([
new Link(rel: 'preconnect', href: 'https://fonts.google.com'),
(new Link(href: '/style.css'))->withAttribute('as', 'style'),
(new Link(href: '/script.js'))->withAttribute('as', 'script'),
]);
// prepare the contents of the response...
return $this->render('homepage/index.html.twig', response: $response);
}
}
从技术上讲,Early Hints 是一个信息性的 HTTP 响应,状态代码为 103
。sendEarlyHints()
方法创建一个状态代码为该代码的 Response
对象,并立即发送其标头。
这样,浏览器可以立即开始下载资源;就像上面示例中的 style.css
和 script.js
文件。sendEarlyHints()
方法还返回 Response
对象,您必须使用该对象来创建从控制器操作发送的完整响应。
最后的想法
在 Symfony 中,控制器通常是一个类方法,用于接受请求并返回 Response
对象。当与 URL 映射时,控制器变得可访问,并且可以查看其响应。
为了方便控制器的开发,Symfony 提供了 AbstractController
。它可以用于扩展控制器类,从而允许访问一些常用工具,例如 render()
和 redirectToRoute()
。AbstractController
还提供了 createNotFoundException()
实用程序,该实用程序用于返回未找到页面的响应。
在其他文章中,您将学习如何从控制器内部使用特定服务,这些服务将帮助您持久化和获取数据库中的对象、处理表单提交、处理缓存等等。
继续前进!
接下来,了解所有关于 使用 Twig 渲染模板 的信息。