Symfony 7.3 includes many small improvements aimed at making developers' lives easier and more productive. This blog post highlights some of the most useful DX (Developer Experience) features added in this release.

Read the second part of this blog post.

Collect All Twig Deprecations

Pierre Ambroise
Contributed by Pierre Ambroise in #60039

The lint:twig command checks the syntax of your Twig templates and reports any errors and deprecations. Previously, it only reported the first deprecation found, so you had to run it repeatedly to list all of them.

In Symfony 7.3, this command has been improved to report all deprecations at once:

1
2
3
4
5
6
7
8
$ php bin/console lint:twig --show-deprecations

  DEPRECATION in /app/templates/index.html.twig (line 5)
  >> Since foo/bar 1.1: Twig filter "deprecated_filter" is deprecated
  DEPRECATION in /app/templates/index.html.twig (line 6)
  >> Since foo/bar 1.1: Twig filter "deprecated_filter" is deprecated

[OK] All 2 Twig files contain valid syntax.

Transform Strings to Pascal Case

Raffaele Carelle
Contributed by Raffaele Carelle in #58545

The String component provides several methods to transform the case of a string (lower(), upper(), camel(), snake(), kebab()). In Symfony 7.3, a new pascal() method has been added to convert strings to PascalCase:

1
2
3
4
// before Symfony 7.3
u('Foo: Bar-baz.')->camel()->title(); // 'FooBarBaz'
// in Symfony 7.3
u('Foo: Bar-baz.')->pascal();         // 'FooBarBaz'

Field id Form Helper

Legendary4226
Contributed by Legendary4226 in #59842

Rendering the id attribute of a form field is a common need in templates. You can already access it via the vars array of each form field, but Symfony 7.3 introduces a new Twig function to simplify this:

1
2
3
4
5
{# before Symfony 7.3 #}
<input id="{{ form.task.vars.id }}" ... />

{# in Symfony 7.3 #}
<input id="{{ field_id(form.task) }}" ... />

noStore Cache Argument

Simon André
Contributed by Simon André in #59301

The #[Cache] attribute allows you to configure various HTTP Cache options. In Symfony 7.3, it has been improved to support the no-store response directive, which tells caches not to store any part of the request or response:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\HttpKernel\Attribute\Cache;

#[Cache(noStore: true)]
final class MyController
{
    public function __invoke(): Response
    {
        // This response will NOT be stored in ANY cache
    }
}

Union Types in Options Resolver

Vincent Langlet
Contributed by Vincent Langlet in #59354

The OptionsResolver component lets you define allowed types for options. Until now, you could only pass an array of basic types (e.g. ['int', 'string']), but not actual union types like (int|string)[].

However, sometimes you have to use the union type (e.g. (int|string)[]) because you can't define the same type information using arrays (e.g. ['string[]', 'int[]'] is not equivalent).

Symfony 7.3 now fully supports PHP-style union types when defining option types.

Improved ImportMap Require Command

Florian Cellier
Contributed by Florian Cellier in #59464

The importmap:require command lets you import third-party JavaScript and CSS packages when using AssetMapper. In Symfony 7.3, this command has been enhanced with a --dry-run option, so you can preview the packages to be installed:

1
$ php bin/console importmap:require --dry-run bootstrap

Clock Mock in URI Signer

Kevin Bond
Contributed by Kevin Bond in #60222

Symfony provides a utility to sign URIs, which can include an expiration date. In Symfony 7.3, the Clock component has been integrated with UriSigner so you can mock the current time in your tests, making them simpler and more reliable.

DatePoint Doctrine Type

Massimiliano Arione
Contributed by Massimiliano Arione in #59900

The DatePoint class was introduced in Symfony 6.4 as a modern alternative to DateTime and DateTimeImmutable. However, Doctrine couldn't use it as a field type directly.

Symfony 7.3 introduces a DatePointType that you can now use in your entities:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Entity/Product.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Clock\DatePoint;

#[ORM\Entity]
class Product
{
    #[ORM\Column]
    private DatePoint $created;

    // ...
}
Published in #Living on the edge