Вышла новая версия Symfony 6.3

Дата публикации: 2023-05-30
Просмотры: 573

Вышла очередная минорная версия Symfony 6.3.
Как и все минорные обновления Symfony поддерживает обратную совместимость.
А это означает, что вы сможете легко обновиться, ничего не меняя в своем коде.

Вышла новая версия Symfony 6.3

Ниже перечислены некоторые изменения для этой версии:

Early Hints

В Symfony 6.3 добавлена поддержка Early Hints, которая позволяет браузерам загружать ассеты, пока сервер подготавливает ответ. Это значительно улучшает производительность.

Early Hints — это один из самых последних и эффективных методов улучшения производительности ваших сайтов и приложений. Early Hints позволяют серверам сообщать браузерам, какие ресурсы (файлы CSS и JavaScript, веб-шрифты и т.д.) следует начать загружать, пока серверы все еще работают над созданием ответа.

С технической точки зрения, Early Hints — это ответ HTTP с кодом состояния, 103 который содержит один или несколько заголовков HTTP, в которых перечислены ресурсы для загрузки или подключения. Например:

103 Early Hints
Link: <https://fonts.google.com>; rel=preconnect
Link: </main.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script

Эти заголовки также включаются в ответ, который серверы отправят позже, чтобы ответить на запрос пользователя. Однако отправив их как можно раньше, браузеры могут начать их загрузку или подготовку к ним, что значительно улучшит производительность.

Пример:

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\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: '/main.css'))->withAttribute('as', 'stylesheet'),
            (new Link(href: '/app.js'))->withAttribute('as', 'script'),
        ]);

        // prepare the contents of the response...

        return $this->render('homepage/index.html.twig', response: $response);
    }
}

HTTP Exception Attributes

Symfony 6.3 представляет два новых атрибута для настройки кода состояния, заголовков и уровня журнала ваших исключений HTTP.

В настоящее время для создания собственных HTTP-исключений необходимо реализовать HttpExceptionInterface (или расширить HttpException класс) и настроить его:

use App\Domain\Exception\Order\OrderNotFound;
use Symfony\Component\HttpKernel\Exception\HttpException;

class OrderNotFound extends HttpException
{
    public static function create(string $id): self
    {
        return new self(
            statusCode: Response::HTTP_NOT_FOUND,
            message: sprintf('The order "%s" could not be found.', $id),
            headers: ['x-header' => 'foo'],
        );
    }
}
# config/packages/exceptions.yaml
framework:
    exceptions:
        App\Domain\Exception\Order\OrderNotFound:
            log_level: 'debug'
            status_code: 404

В Symfony 6.3 приведенный выше код и конфигурация все еще работают, но вы можете при желании заменить их следующими атрибутами PHP:

use App\Domain\Exception\Order\OrderNotFound;
use Psr\Log\LogLevel;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
use Symfony\Component\HttpKernel\Attribute\WithLogLevel;

#[WithHttpStatus(Response::HTTP_NOT_FOUND, headers: ['x-header' => 'foo'])]
#[WithLogLevel(LogLevel::DEBUG)]
class OrderNotFound extends \Exception
{
    // ...

    public function getMessage(): string
    {
        return sprintf('The order "%s" could not be found.', $this->order->getId());
    }
}

Mapping Request Data to Typed Objects

Symfony 6.3 представляет два новых атрибута PHP для сопоставления данных запроса с объектами, такими как DTO, и автоматически проверяет их.

Во-первых, #[MapRequestPayload] атрибут берет данные из $_POST и пытается заполнить ими заданный типизированный объект.

Рассмотрим следующий класс DTO:

use Symfony\Component\Validator\Constraints as Assert;

class ProductReviewDto
{
    public function __construct(
        #[Assert\NotBlank]
        #[Assert\Length(min: 10, max: 500)]
        public readonly string $comment,

        #[Assert\GreaterThanOrEqual(1)]
        #[Assert\LessThanOrEqual(5)]
        public readonly int $rating,
    ) {
    }
}

Symfony 6.3 автоматически сопоставит данные запроса с объектом DTO и проверит их:

use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;

class ProductApiController
{
    public function __invoke(
        #[MapRequestPayload] ProductReviewDto $productReview,
    ): Response {

        // here, $productReview is a fully typed representation of the request data

    }
}

Targeted Value Resolvers

Symfony 6.3 представляет новый атрибут ValueResolver для явного выбора преобразователя аргументов для использования в аргументах контроллера.

Рассмотрим следующий пример:

// src/Controller/SessionController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;

class SessionController
{
    #[Route('/')]
    public function __invoke(SessionInterface $session = null): Response
    {
        // ...
    }
}

Symfony будет вызывать все встроенные преобразователи аргументов по приоритету, пока один из них не предоставит значение для этого аргумента. В этом примере SessionValueResolver (priority 50) будет вызываться перед DefaultValueResolver (priority -100). Вот почему $session аргумент будет иметь либо текущий Session объект, либо null.

Если вы знаете, что в вашем приложении всегда будет сессия, то можно было бы сделать так:

// ...
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;

class SessionController
{
    #[Route('/')]
    public function __invoke(
        #[ValueResolver(SessionValueResolver::class)]
        SessionInterface $session
    ): Response
    {
        // ...
    }
}

Request Payload

Symfony 6.3 добавляет метод getPayload() к объекту запроса, чтобы получить их содержимое POST и/или содержимое тела.

Обычно в Symfony для получения параметров запроса используются следующие методы:

$request->query->get('foo');   // similar to $_GET['foo']
$request->request->get('foo'); // similar to $_POST['foo']
$request->cookies->get('foo'); // similar to $_COOKIES['foo']
$request->files->get('foo');   // similar to $_FILES['foo']

Теперь можно использовать новый метод getPayload() для получения всего массива входящих параметров:

// Example 1:
// the request has some POST data (e.g. `foo: bar` sent as `application/x-www-form-urlencoded`)
$request->getPayload()->all();
// returns the PHP array: ['foo' => 'bar']

// Example 2:
// the request has body contents as some JSON-encoded data (e.g. `{"foo": "bar"}`)
$request->getPayload()->all();
// returns the JSON-decoded data as a PHP array: ['foo' => 'bar']

Enum Improvements

Symfony 6.3 добавляет поддержку перечислений в выражениях, деревьях конфигурации и параметрах маршрутов.

Пример для конфигурации:

enum OrderDelivery: string
{
    case Standard = 'standard';
    case Expedited = 'expedited';
    case Priority = 'priority';
}

$rootNode
    ->children()
        ->enumNode('delivery')
            // You can provide all values of the enum...
            ->values(OrderDelivery::cases())

            // ... or you can pass only some values next to other scalar values
            ->values([OrderDelivery::Priority, OrderDelivery::Standard, 'other', false])
        ->end()
    ->end()
;

В Symfony 6.2 была добавлены поддержка использования перечислений в качестве аргументов маршрута, чтобы Symfony автоматически преобразовывал их в связанный с ними перечисления.
В Symfony 6.3 улучшили эту функцию, позволяя использовать enum в качестве значения по умолчанию для аргументов маршрута:

// src/Controller/OrderController.php
namespace App\Controller;

use App\Config\Order\OrderStatus;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class OrderController extends AbstractController
{
    #[Route('/orders/list/{status}', name: 'list_orders_by_status')]
    public function list(OrderStatus $status = OrderStatus::Paid): Response
    {
        // ...
    }
}

Login and Logout Improvements

Symfony 6.3 позволяет перенаправлять пользователей после их программного входа в систему, добавляет поддержку «Запомнить меня» для входа в систему JSON и позволяет очищать данные сайта после выхода пользователя из системы.

В Symfony 6.3 login() метод теперь также возвращает этот Response объект:

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Response;

class RegistrationController extends AbstractController
{
    public function verifyUserEmail(Security $security): Response
    {
        // ...

        $redirectResponse = $security->login($user);

        return $redirectResponse;
    }
}

В Symfony 6.3 добавлена поддержка HTTP-заголовка Clear-Site-Data через logout конфигурацию ваших брандмауэров:

security:
    # ...
    firewalls:
        main:
            # ...
            logout:
                path: app_logout
                # the available options are 'cache', 'cookies', 'storage', 'executionContexts'
                # you can also use the '*' wildcard to clear all data
                clear_site_data:
                    - cache
                    - storage
                    - executionContexts

Query Parameters Mapper

Symfony 6.3 включает новый атрибут MapQueryParameter для сопоставления и проверки отдельных параметров строки запроса с аргументами контроллера.

Symfony позволяет сопоставлять параметры маршрута с аргументами контроллера, начиная с первой версии.
В Symfony 6.3 вы также сможете сопоставлять параметры строки запроса с конкретными аргументами контроллера.
Для этого используйте новый #[MapQueryParameter] атрибут:

#[Route(path: '/', name: 'admin_dashboard')]
public function indexAction(
    #[MapQueryParameter] array $ids,
    #[MapQueryParameter] string $firstName,
    #[MapQueryParameter] bool $required,
    #[MapQueryParameter] int $age,
    #[MapQueryParameter] string $category = '',
    #[MapQueryParameter] ?string $theme = null,
)

Если строка запроса входящего запроса равна /?ids[]=1&ids[]=2&firstName=Ruud&required=3&age=123, этот код заставит аргументы контроллера иметь следующие значения:

$ids = ['1', '2']
$firstName = "Ruud"
$required = false
$age = 123
$category = ''
$theme = null

Атрибут #[MapQueryParameter] выполняет преобразование параметра строки запроса (который всегда является строкой) в правильный тип PHP в соответствии с типами аргументов вашего контроллера (это похоже на вызов $request->query->getInt('...'), $request->query->getAlpha('...') и т.д., но автоматизировано Symfony).

Для более продвинутых вариантов использования вы можете использовать filterпараметр атрибута для проверки сведений о типе данных:

// this ensures that the array only contains integer values
#[MapQueryParameter(filter: \FILTER_VALIDATE_INT)]
array $ids

// this ensures that the string follows some specific pattern
#[MapQueryParameter(filter: \FILTER_VALIDATE_REGEXP, options: ['regexp' => '/^\w++$/'])]
string $name

Об этих и других улучшениях вы можете почитать по ссылке ниже.
Ссылка на источник: https://symfony.com/blog/symfony-6-3-curated-new-features

Поделиться статьей:

Комментарии (1):

Авторизуйтесь, для того, чтобы оставить комментарий. Войти на сайт
avatar
Григорий Стерин
2023-05-31 12:38
Наконец-то долгожданное обновление ;)
Подписка на новости
Узнавайте о новых статьях первыми.
Профиль