Контроллер
Дата обновления перевода 2023-07-25
Контроллер
Контроллер - это созданная вами PHP-функция, которая смотрит на объект Request
создает и возвращает объект Response
. Ответ может быть HTML-страницей,
JSON, XML, сохраняемым файлом, редиректом, ошибкой 404 или чем-то другим.
Контроллер может запускать любую произвольную логику, которая нужна вашему приложению
для отображения содержимого страницы.
Tip
Если вы еще не создали свою первую рабочую страницу, просмотрте главу создание страницы и потом возвращайтесь!
Простой контроллер
В то время как контроллер может быть любой 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\Annotation\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://localhost: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
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; // ... public function index(): RedirectResponse { // перенаправляет по пути "homepage" return $this->redirectToRoute('homepage'); // redirectToRoute - это сокращение для: // return new RedirectResponse($this->generateUrl('homepage')); // делает постоянный - 301-й редирект return $this->redirectToRoute('homepage', [], 301); // перенаправлять по пути с параметрами return $this->redirectToRoute('app_lucky_number', ['max' => 10]); // перенаправляет по пути и сохраняет изначальные параметры запроса return $this->redirectToRoute('blog_show', $request->query->all()); // перенаправляет на внешний сайт return $this->redirect('http://symfony.com/doc'); }
Caution
Метод redirect()
никак не проверяет место назначеня. Если вы перенаправляете
по URL, предоставленному конечными пользователями, ваше приложение может быть
открыто к уязвимости безопасности невалидированных редиректов.
Отображение шаблонов
Если вы выдаёте HTML, вам пригодится умение отображать шаблоны. Метод render()
отображает шаблон и помещает его содержимое в объект Response
для вас:
1 2
// отображает 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
Tip
Если вам нужен контроль над точным значением аргумента, или потребовать параметр,
вы можете использовать атрибут #[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,
// внедрить конкретный сервис логгера
#[Autowire(service: 'monolog.logger.request')]
LoggerInterface $logger,
// или внедрить значения параметра
#[Autowire('%kernel.project_dir%')]
string $projectDir
): Response
{
$logger->info('We are logging!');
// ...
}
}
Вы можете прочитать больше об этом атрибуте в .
6.1
Атрибут #[Autowire]
был представлен в Symfony 6.1.
Как и со всеми сервисами, вы также можете использовать обычное внедрение конструктора в ваших контроллерах.
Чтобы узнать больше о сервисах, см. статью Сервис-контейнер.
Генерирование контроллеров
Для экономии времени, вы можете установить Symfony Maker и сказать Symfony сгенерировать новый класс контроллера:
1 2 3 4
$ php bin/console make:controller BrandNewController
created: src/Controller/BrandNewController.php
created: templates/brandnew/index.html.twig
Если вы хотите сгенеритьвать полный CRUD с привязкой к Doctrine entity, запускайте:
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
{
// извлечь объект из DB
$product = ...;
if (!$product) {
throw $this->createNotFoundException('The product does not exist');
// вышенаписанное - просто сокращение для:
// вызвать новый NotFoundHttpException('Продукт не существует');
}
return $this->render(...);
}
Метод createNotFoundException() - это лишь сокращение для создания специального объекта NotFoundHttpException, который в конечном счете запускает ответ 404 внутри Symfony.
Если вы вызовете исключение, расширяющее экземпляр HttpException, Symfony будет использовать соответствующий статус-код HTTP. Иначе ответ будет выдавать статус-код HTTP 500:
1 2
// это исключение сгенерирует ошибку с HTTP 500
throw new \Exception('Что-то пошло не так!');
В обоих случаях, конечному пользователю отображается страница ошибки, а разработчику отображается полная страница отладки ошибки (например, когда вы в режиме "отладки" - см. ).
Для настройки страницы ошибки, отображаемую пользователю, см. статью Как настроить страницы ошибок.
Объект запроса в качестве аргумента контроллера
Что вы будете делать, если вам понадобится узнать параметры запроса, заголовок
запроса или получить доступ к загруженному файлу? Вся эта информация в Symfony
содержится в объекте 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.
Управление сессией
В сессии пользователя можно хранить специальные сообщения, называемые "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()) {
// сделать какую-то обработку
$this->addFlash(
'notice',
'Your changes were saved!'
);
// $this->addFlash() эквивалентно $request->getSession()->getFlashBag()->add()
return $this->redirectToRoute(/* ... */);
}
return $this->render(/* ... */);
}
Прочитайте это , чтобы узнать больше об использовании сессий.
Объект Request и Response
Как уже упоминалось раньше , Symfony будет
передавать объект Request
любому аргументу контроллера, тип которого указан с
помощью класса Request
:
use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse;
public function index(Request $request): Response { $request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(['en', 'fr']);
// извлекает переменные GET и POST, соответственно $request->query->get('page'); $request->request->get('page');
// извлекает переменные SERVER $request->server->get('HTTP_HOST');
// извлекает экземпляр UploadedFile, идентифицированный foo $request->files->get('foo');
// извлекает значение COOKIE
$request->cookies->get('PHPSESSID');// извлекает HTTP-заголовок запроса, с нормализованными нижнестрочными ключами $request->headers->get('host'); $request->headers->get('content-type');
}
Класс Request
имеет несколько публичных свойств и методов, которые возвращают любую
необходимую информацию о запросе.
Как и Request
, объект Response
имеет публичное свойство headers
.
Этот объект имеет тип ResponseHeaderBag
и предоставляет методы для получения и установки заголовков ответа. Имена заголовков
нормализованы. В результате имя Content-Type
эквивалентно имени content-type
или content_type
.
В Symfony контроллер должен возвращать объект Response
:
1 2 3 4 5 6 7 8
use Symfony\Component\HttpFoundation\Response;
// создаёт простой Response со статус-кодом (по умолчанию)
$response = new Response('Hello '.$name, Response::HTTP_OK);
// создаёт CSS-ответ со статус-кодом 200
$response = new Response('<style> ... </style>');
$response->headers->set('Content-Type', 'text/css');
Для облегчения этой задачи предусмотрены различные объекты ответа, предназначенные для разных
типов ответов. Некоторые из них приведены ниже. Чтобы узнать больше о Request
и Response
(и различных классах Response
), см. Документацию компонента HttpFoundation .
Доступ к значениям конфигурации
Чтобы получить значение любого параметра конфигурации
из контроллера, используйте метод-помощник 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
{
// возвращает '{"username":"jane.doe"}' и устанавливает правильный заголовок Content-Type
return $this->json(['username' => 'jane.doe']);
// сокращение определяет три опциональных аргумента
// return $this->json($data, $status = 200, $headers = [], $context = []);
}
Если в вашем приложении включен сервис сериализации, то он будет использоваться для сериализации данных в JSON. В противном случае, используется функция json_encode.
Потоковые ответы файлов
Вы можете использовать помощника file(), чтобы обслуживать файл изнутри контроллера:
1 2 3 4 5 6 7 8
use Symfony\Component\HttpFoundation\BinaryFileResponse;
// ...
public function download(): BinaryFileResponse
{
// отправить содержание файла и заставить браузер скачать его
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
{
// загрузить файл из файловой системы
$file = new File('/path/to/some_file.pdf');
return $this->file($file);
// переименовать скачанный файл
return $this->file($file, 'custom_name.pdf');
// отобразить содержание файла в браузере вместо того, чтобы скачивать его
return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}
Заключение
В Symfony, контроллер - это обычно метод класса, который используется для приёма
запросов и выдачи объекта Response
. Если связать его с URL, контроллер
становится доступным и его ответ можно увидеть.
Для помощи в разработке контроллеров, Symfony предоставляет
AbstractController
. Он может быть использован для расширения класса контроллера давая
доступ к часто используемым функциям такие как render()
и
redirectToRoute()
. AbstractController
также предоставляет метод
createNotFoundException()
, который используется для возврата ответа "404. Не найдено"
В других статьях вы узнаете, как использовать спецаильные сервисы изнутри вашего контроллера, что поможет вам сохранять и получать объекты из базы данных, обрабатывать отправленные формы, работать с кэшем и т.д.
Продолжайте!
Далее, узнайте всё об Отображении шаблонов с помощью Twig.