Дата обновления перевода: 2021-05-27*
Контроллер¶
Контроллер - это созданная вами PHP-функция, которая смотрит на объект Request
создает и возвращает объект Response
. Ответ может быть HTML-страницей,
JSON, XML, сохраняемым файлом, редиректом, ошибкой 404 или чем-то другим.
Контроллер может запускать любую произвольную логику, которая нужна вашему приложению
для отображения содержимого страницы.
Tip
Если вы еще не создали свою первую рабочую страницу, просмотрте главу создание страницы и потом возвращайтесь!
Простой контроллер¶
В то время как контроллер может быть любой PHP-сущностью (функцией, методом объекта
или Closure
), обычно контроллер - это метод внутри класса контроллера:
// 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($max)
{
$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
. - Строчка 12: Методу действия разрешено иметь аргумент
$max
благодаря символу подстановки в маршруте{max}
. - Строчка 16: Контроллер создает и возвращает объект
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 для заданного маршрута:
$url = $this->generateUrl('app_lucky_number', ['max' => 10]);
Перенаправление¶
- Если вы хотите перенаправить пользователя на другую страницу, используйте методы
redirectToRoute()
иredirect()
:use Symfony\Component\HttpFoundation\RedirectResponse; // ... public function index() { // редирект на путь "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
для вас:
// отображает templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);
Шаблонизирование и Twig обяснены детальнее в статье Создание и использование шаблонов.
Получение сервисов¶
Symfony по умолчанию наполнена большим количеством полезных объектов, называемых сервисами. Они используются для отображения шаблонов, отправки почты, запросов к базе данных и любой другой “работы”, которую вы можете себе представить.
Если вам нужен сервис в контроллере, укажите класс или интерфейс аргумента. Symfony автоматически передаст вам необходимый сервис:
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
|
Если вам необходим контроль над точным значением аргумента, вы можете cвязать аргумент с его именем:
- YAML
1 2 3 4 5 6 7 8 9 10 11 12
# config/services.yaml services: # ... # explicitly configure the service App\Controller\LuckyController: tags: [controller.service_arguments] bind: # for any $logger argument, pass this specific service $logger: '@monolog.logger.doctrine' # for any $projectDir argument, pass this parameter value $projectDir: '%kernel.project_dir%'
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<!-- config/services.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <!-- ... --> <!-- Explicitly configure the service --> <service id="App\Controller\LuckyController"> <tag name="controller.service_arguments"/> <bind key="$logger" type="service" id="monolog.logger.doctrine" /> <bind key="$projectDir">%kernel.project_dir%</bind> </service> </services> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11
// config/services.php use App\Controller\LuckyController; use Symfony\Component\DependencyInjection\Reference; $container->register(LuckyController::class) ->addTag('controller.service_arguments') ->setBindings([ '$logger' => new Reference('monolog.logger.doctrine'), '$projectDir' => '%kernel.project_dir%' ]) ;
Как и со всеми сервисами, вы можете использовать обычное внедрение через конструктор в ваших контроллерах.
Чтобы узнать больше о сервисах, см. статью Service Container.
Генерация контроллеров¶
Для экономии времени, вы можете установить 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
|
New in version 1.2: Команда make:crud
появилась в MakerBundle 1.2.
Управление ошибками и страницами 404¶
Когда что-то не найдено, вы должны вернуть ответ 404. Чтобы сделать это, вызовите специальный тип исключения:
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:
// это исключение сгенерирует ошибку с HTTP 500
throw new \Exception('Что-то пошло не так!');
В обоих случаях, конечному пользователю отображается страница ошибки, а разработчику отображается полная страница отладки ошибки (например, когда вы в режиме “отладки” - см. Окружения конфигурации).
Для настройки страницы ошибки, отображаемую пользователю, см. статью How to Customize Error Pages.
Объект Request в качестве аргумента контроллера¶
Что вы будете делать, если вам понадобится узнать параметры запроса, заголовок
запроса или получить доступ к загруженному файлу? Вся эта информация в Symfony
содержится в объекте Request
. Чтобы получить доступ к этой информации в контроллере,
просто добавьте его в качестве аргумента и добавьте тип Request:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...
public function index(Request $request, string $firstName, string $lastName): Response
{
$page = $request->query->get('page', 1);
// ...
}
Продолжайте читать для более детальной информации об использовании объекта Request.
Управление сессиями¶
Symfony предоставляет сервис сессии, который вы можете использовать для хранения информации о пользователе между запросами. Сессии включены по умолчанию, но запустятся только, когда вы начнёте читать или записывать в них.
Хранение сессии и другую конфигурацию можно контролировать в
конфигурации framework.session в
config/packages/framework.yaml
.
Для получения доступа к сессии добавьте аргумент и обозначьте его тип как
SessionInterface
:
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
// ...
public function index(SessionInterface $session): Response
{
// сохраняет атрибут для переиспользования позже в запросе пользователя
$session->set('foo', 'bar');
// получает атрибут установленный другим контроллером в другом запросе
$foobar = $session->get('foobar');
// использует значение по умолчению, если атрибут не существует
$filters = $session->get('filters', []);
// ...
}
Сохранённые атрибуты будут храниться в сессии до окончания сессии пользователя.
Чтобы узнать больше, см. Sessions.
Flash-сообщения¶
Вы можете также сохранять специальные сообщения, которые называют flash-сообщениями, в пользовательской сессии. Согласно замыслу, flash-сообщения предполагается использовать ровно один раз: они автоматически исчезают из сессии, как только вы их возвращаете. Эта особенность делает flash-сообщения особенно удобными для хранения пользовательских оповещений.
Для примера представьте, что вы обрабатываете отправку формы:
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',
'Ваши изменения сохранены!'
);
// $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()
return $this->redirectToRoute(...);
}
return $this->render(...);
}
После обработки запроса контроллер устанавливает flash-сообщение в сессии и затем
выполняет редирект. Ключ к сообщению (notice
в этом примере) может быть любым:
вы будете его использовать для того, чтобы получить доступ к самому сообщению.
В шаблоне следующей страницы (или ещё лучше, в вашем базовом шаблоне),
прочитайте любое flash-сообщение из сессии используя метод flashes()
предоставляемый
глобальной переменной app в Twig:
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 | {# templates/base.html.twig #}
{# прочитать и отобразить только один тип флеш-сообщения #}
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
{# прочитать и отобразить несколько типов флеш-сообщений #}
{% for label, messages in app.flashes(['success', 'warning']) %}
{% for message in messages %}
<div class="flash-{{ label }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
{# прочитать и отобразить все флеш-сообщения #}
{% for label, messages in app.flashes %}
{% for message in messages %}
<div class="flash-{{ label }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
|
Обычно используют notice
, warning
и error
в качестве ключей для
разных типов flash-сообщений, но вы можете использовать любой ключ, который вам подходит.
Tip
В качестве альтернативы вы можете использовать метод
peek()
чтобы получить сообщение, не удаляя его.
Объекты Request и Response¶
Как упоминалось ранее, Symfony
передаст объект Request
любому аргументу контроллера, который будет
типизирован по классу Request
:
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']);
// извлекает переменные 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
.
Единственное, что требуется от контроллера - это возвращать объект Response
:
use Symfony\Component\HttpFoundation\Response;
// создаёт простой Response со статус-кодом 200 (по умолчанию)
$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()
:
// ...
public function index(): Response
{
$contentsDir = $this->getParameter('kernel.project_dir').'/contents';
// ...
}
Возвращение JSON-ответа¶
Чтобы вернуть JSON из контроллера, используйте метод json()
. Он возвращает
специальный объект JsonResponse
, который автоматически превращает данные в json:
use Symfony\Component\HttpFoundation\Response;
// ...
public function index(): Response
{
// возвращает '{"username":"jane.doe"}' и устанавливает правильный заголовок Content-Type
return $this->json(['username' => 'jane.doe']);
// сокращение определяет три необязательных аргумента
// return $this->json($data, $status = 200, $headers = [], $context = []);
}
Если в вашем приложении включен сервис сериализации,
то он будет использован для сериализации данных в JSON. Иначе
будет использована функция json_encode
.
Потоковая передача файлов ответов¶
Вы можете использовать метод file()
,
чтобы выдавать файл из контроллера:
use Symfony\Component\HttpFoundation\Response;
// ...
public function download(): Response
{
// отправить содержание файла и заставить браузер скачать его
return $this->file('/path/to/some_file.pdf');
}
Метод file()
предоставляет некоторые аргументы, чтобы сконфигурировать его поведение:
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
// ...
public function download(): Response
{
// загрузить файл из файловой системы
$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.
Узнайте больше о контроллерах¶
- Extending Action Argument Resolving
- Расширения разрешения аргумента действия
- How to Customize Error Pages
- Как настроить страницы ошибок
- Як налаштувати сторінки помилок
- How to Forward Requests to another Controller
- Как пересылать запросы другому контроллеру
- How to Define Controllers as Services
- Как определять контроллеры как сервисы
- How to Create a SOAP Web Service in a Symfony Controller
- Как создать SOAP веб-сервис в контроллере Symfony
- How to Upload Files
- Как загружать файлы
- Як завантажувати файли
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.