Вышла новая версия Symfony 6.3
Вышла очередная минорная версия Symfony 6.3.
Как и все минорные обновления Symfony поддерживает обратную совместимость.
А это означает, что вы сможете легко обновиться, ничего не меняя в своем коде.
Ниже перечислены некоторые изменения для этой версии:
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