Контроллеры в фреймворке Symfony.

Контроллер — это созданная вами функция 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(), который используется для возврата ответа «страница не найдена».

В других статьях вы узнаете, как использовать определенные службы внутри вашего контроллера, которые помогут вам сохранять и извлекать объекты из базы данных, обрабатывать отправку форм, обрабатывать кэширование и многое другое.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *