Контроллер — это созданная вами функция PHP, которая считывает информацию из объекта Request, создает и возвращает объект Response. Ответом может быть страница HTML, JSON, XML, загрузка файла, перенаправление, ошибка 404 или что-то еще.
Контроллер выполняет любую произвольную логику, необходимую вашему приложению для визуализации содержимого страницы.
Создание простого контроллера в Symfony.
В то время как контроллер может быть любым типом callable PHP (функция, метод объекта или замыкание), контроллер в Symfony является методом внутри класса контроллера:
// 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 с ним через маршрут. Это было сделано выше с помощью аннотации маршрута @Route(«/lucky/number/{max}»).
Чтобы увидеть свою страницу, перейдите по этому URL в вашем браузере: http://localhost:8000/lucky/number/100
Базовый класс для контроллеров в Symfony.
Чтобы помочь процессу разработки, Symfony поставляется с необязательным базовым классом контроллера, который называется AbstractController. Любой пользовательский контроллер может его расширить, чтобы получить доступ к вспомогательным методам.
Добавьте оператор use поверх вашего класса контроллера, а затем измените LuckyController, чтобы расширить его:
// src/Controller/LuckyController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class LuckyController extends AbstractController
{
// ...
}
Теперь у вас есть доступ к таким методам, как $this->render() и многим другим, о которых будет сказано ниже.
Генерация URLs.
Метод generateUrl() — это просто вспомогательный метод, который генерирует URL для данного маршрута:
$url = $this->generateUrl('app_lucky_number', ['max' => 10]);
Redirecting.
Если вы хотите перенаправить пользователя на другую страницу, используйте методы redirectToRoute() и redirect() из класса базового контроллера:
use Symfony\Component\HttpFoundation\RedirectResponse;
// ...
public function index()
{
// redirects to the "homepage" route
return $this->redirectToRoute('homepage');
// redirectToRoute is a shortcut for:
return new RedirectResponse($this->generateUrl('homepage'));
// does a permanent - 301 redirect
return $this->redirectToRoute('homepage', [], 301);
// 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 externally
return $this->redirect('http://symfony.com/doc');
}
Метод redirect() никоим образом не проверяет свое назначение. Если вы перенаправляете на URL-адрес, предоставленный конечными пользователями, ваше приложение может быть открыто для уязвимости безопасности, связанной с непроверенными перенаправлениями.
Рендеринг шаблонов
Если вы работаете с HTML, вы захотите отрендерить шаблон. Метод render() рендерит шаблон и помещает содержимое результат рендеринга в объект Response:
Получение Сервисов в контроллерах.
Symfony поставляется с множеством полезных объектов, называемых сервисами. Они используются для рендеринга шаблонов, отправки электронных писем, запросов к базе данных и любой другой «работы», о которой вы только можете подумать.
Если вам нужен сервис в контроллере, введите подсказку с именем класса (или интерфейса). Symfony автоматически передаст вам необходимый сервис:
use Psr\Log\LoggerInterface;
// ...
/**
* @Route("/lucky/number/{max}")
*/
public function number($max, LoggerInterface $logger)
{
$logger->info('We are logging!');
// ...
}
Какие еще сервисы вы можете получить в контроллерах используя тайп-хинт? Чтобы увидеть их, используйте консольную команду debug:autowiring:
php bin/console debug:autowiring
Если вам нужен контроль над точным значением аргумента, вы можете связать аргумент по его имени в конфигурационном файле services.yaml:
# config/services.yaml
services:
# ...
# explicitly configure the service
App\Controller\LuckyController:
public: true
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%'
Генерация контроллеров
Чтобы сэкономить время, вы можете установить бандл Symfony Maker и попросить Symfony создать новый класс контроллера:
php bin/console make:controller BrandNewController
created: src/Controller/BrandNewController.php
created: templates/brandnew/index.html.twig
Если вы хотите сгенерировать весь CRUD из сущности Doctrine, используйте:
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. Для этого выведите специальный тип исключения NotFoundHttpException:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// ...
public function index()
{
// 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, который в конечном итоге инициирует ответ HTTP 404 внутри Symfony.
Если вы сгенерируете исключение, которое расширяет или является экземпляром HttpException, Symfony будет использовать соответствующий код состояния HTTP. В противном случае ответ будет иметь код состояния HTTP 500:
// this exception ultimately generates a 500 status error
throw new \Exception('Something went wrong!');
В каждом случае страница с ошибками показывается конечному пользователю, а полная страница с ошибками отладки показывается разработчику (т.е. когда вы находитесь в режиме « Debug».
Объект запроса Request как аргумент контроллера.
Что если вам нужно прочитать параметры запроса, получить заголовок запроса или получить доступ к загруженному файлу? Эта информация хранится в объекте запроса Request Symfony. Чтобы получить доступ к нему в вашем контроллере, добавьте его в качестве аргумента и введите подсказку с помощью класса Request:
use Symfony\Component\HttpFoundation\Request;
public function index(Request $request, $firstName, $lastName)
{
$page = $request->query->get('page', 1);
// ...
}
Управление сессией в контроллере Symfony.
Symfony предоставляет сервис сессии, который вы можете использовать для хранения информации о пользователе между запросами. Сессия включена по умолчанию, но будет запущена, только если вы читаете или пишете в нее.
Хранение сессии и другие настройки можно контролировать в конфигурации framework.session в config/packages/framework.yaml.
Чтобы получить сессию, добавьте аргумент и введите тайп-хинт с помощью SessionInterface:
use Symfony\Component\HttpFoundation\Session\SessionInterface;
public function index(SessionInterface $session)
{
// stores an attribute for reuse during a later user request
$session->set('foo', 'bar');
// gets the attribute set by another controller in another request
$foobar = $session->get('foobar');
// uses a default value if the attribute doesn't exist
$filters = $session->get('filters', []);
}
Сохраненные атрибуты остаются в сессии до конца сессии этого пользователя.
Флэш-сообщения в контроллере Symfony.
Вы также можете хранить специальные сообщения, называемые «флэш» сообщениями, в сеансе пользователя. По сути, flash-сообщения предназначены для использования только один раз: они исчезают из сеанса сессии автоматически, как только вы их извлекаете. Эта функция делает «флеш» сообщения особенно полезными для хранения пользовательских уведомлений.
Например, представьте, что вы обрабатываете отправку формы:
use Symfony\Component\HttpFoundation\Request;
public function update(Request $request)
{
// ...
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(...);
}
После обработки запроса контроллер устанавливает флэш-сообщение в сеансе, а затем перенаправляет. Ключ сообщения (обратите внимание в этом примере) может быть любым: вы будете использовать этот ключ для получения сообщения.
В шаблоне следующей страницы (или, что еще лучше, в базовом шаблоне макета) прочитайте все флеш-сообщения из сеанса, используя метод flashes(), предоставленный глобальной переменной приложения Twig:
{# templates/base.html.twig #}
{# read and display just one flash message type #}
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
{# read and display several types of flash messages #}
{% for label, messages in app.flashes(['success', 'warning']) %}
{% for message in messages %}
<div class="flash-{{ label }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
{# read and display all flash messages #}
{% for label, messages in app.flashes %}
{% for message in messages %}
<div class="flash-{{ label }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
Обычно в качестве ключа для различных типов флеш-сообщений используются notice, warning и error, но вы можете использовать любой имя для ключа, соответствующий вашим потребностям.
Вместо этого вы можете использовать метод peek() для извлечения сообщения.
Объекты Request и Response в контроллере Symfony.
Как упоминалось ранее, Symfony будет передавать объект Request любому аргументу контроллера, который объявляет тайп-хинт в классе Request:
use Symfony\Component\HttpFoundation\Request;
public function index(Request $request)
{
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(['en', 'fr']);
// retrieves GET and POST variables respectively
$request->query->get('page');
$request->request->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 есть свойство public headers. Этот объект имеет тип ResponseHeaderBag и предоставляет методы для получения и установки заголовков ответа. Имена заголовков нормализованы. В результате имя Content-Type эквивалентно имени content-type или content_type.
В Symfony контроллер обязан возвращать объект Response:
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');
Чтобы облегчить это, различные объекты ответа включены для адресации различных типов ответа. Некоторые из них упомянуты ниже.
Доступ к значениям конфигурации в контроллере.
Чтобы получить значение любого параметра конфигурации из контроллера, используйте вспомогательный метод getParameter():
// ...
public function index()
{
$contentsDir = $this->getParameter('kernel.project_dir').'/contents';
// ...
}
Возвращение ответа JSON из контроллера Symfony.
Чтобы вернуть JSON из контроллера, используйте вспомогательный метод json(). Это возвращает объект JsonResponse, который автоматически кодирует данные:
// ...
public function index()
{
// 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.
Streaming файла из контроллера.
Вы можете использовать вспомогательный метод file() для возврата файла внутри метода контроллера:
public function download()
{
// send the file contents and force the browser to download it
return $this->file('/path/to/some_file.pdf');
}
Вспомогательный метод file() предоставляет несколько аргументов для настройки своего поведения:
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
public function download()
{
// 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);
}
Резюмируем.
В Symfony контроллер обычно является методом класса, который используется для приема Request и возврата объекта Response. При сопоставлении с URL-адресом контроллер становится доступным и его ответ можно просмотреть.
Для облегчения разработки контроллеров Symfony предоставляет AbstractController. Его можно использовать для расширения класса контроллера, предоставляя доступ к некоторым часто используемым методам, таким как render() и redirectToRoute(). AbstractController также предоставляет метод createNotFoundException(), который используется для возврата ответа «страница не найдена».
В других статьях вы узнаете, как использовать определенные службы внутри вашего контроллера, которые помогут вам сохранять и извлекать объекты из базы данных, обрабатывать отправку форм, обрабатывать кэширование и многое другое.