Symfony includes tens of PHP attributes so you can define metadata next to your code. In Symfony 7.1 we've added some new attributes and in this article we'll showcase two of them related to the DependencyInjection component.
AutowireMethodOf
Attribute
Consider a Symfony controller that needs to call a getCommentPaginator()
method
from a Doctrine repository called CommentRepository
. You could inject the
repository class in the controller action or its constructor. Another alternative
is to use the existing #[AutowireCallable] attribute to do this:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
use App\Repository\CommentRepository;
class SomeController
{
public function showComments(
#[AutowireCallable(service: CommentRepository::class, method: 'getCommentPaginator')]
\Closure $getCommentPaginator,
) {
// ...
}
}
In Symfony 7.1 we're introducing a new AutowireMethodOf attribute to simplify this even more:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\DependencyInjection\Attribute\AutowireMethodOf;
use App\Repository\CommentRepository;
class SomeController
{
public function showComments(
#[AutowireMethodOf(service: CommentRepository::class)]
\Closure $getCommentPaginator,
) {
// ...
}
}
The method name is inferred from the argument name so you don't need to define it
explicitly as in #[AutowireCallable]
. Moreover, if you defined an interface
like this:
1 2 3 4
interface GetCommentPaginatorInterface
{
public function __invoke(Conference $conference, int $page): Paginator;
}
The previous example could be refactored like this:
1 2
#[AutowireMethodOf(CommentRepository::class)]
GetCommentPaginatorInterface $getCommentPaginator,
The benefits of the new #[AutowireMethodOf]
attribute are:
- Increased flexibility: it allows injecting specific methods as
Closures
, providing greater control over what functionality is injected; - Improved readability: thanks to the attribute, the intention behind the dependency injection is made explicit, improving code clarity;
- Enhanced modularity: it decouples services from direct dependencies on specific class methods, making the codebase more maintainable and testable.
AutowireInline
Attribute
Service autowiring allows you to manage services in Symfony applications with
minimal configuration. However, sometimes some services need some manual configuration.
In current Symfony applications, you must define that configuration in YAML, XML
or PHP format in a file stored in config/
.
In Symfony 7.1 we're introducing an #[AutowireInline]
attribute so you can
define services right inside the related PHP class. Consider the following
service:
1 2 3 4 5 6 7 8 9 10
class SomeSourceAwareLogger
{
public function __construct(
private readonly LoggerInterface $logger,
private readonly string $someSource,
) {
}
// ...
}
To inject this service in another one and configure its constructor arguments,
you no longer have to edit the services configuration file. You can use the
#[AutowireInline]
attribute as follows:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\DependencyInjection\Attribute\AutowireInline;
// ...
class SomeClass
{
public function __construct(
#[AutowireInline(class: SomeSourceAwareLogger::class, args: [new Reference(LoggerInterface::class), 'bar'])]
public SomeSourceAwareLogger $someSourceAwareLogger,
) {
}
}
If you have defined a factory, use the factory
option of the attribute to
define the details:
1 2 3 4 5 6 7 8 9 10 11 12
class SomeClass
{
public function __construct(
#[AutowireInline(
class: SomeSourceAwareLogger::class,
factory: [SomeSourceAwareLoggerFactory::class, 'staticCreate'],
args: [new Reference(LoggerInterface::class), 'someParam'],
)]
public SomeSourceAwareLogger $someSourceAwareLogger,
) {
}
}
Using these new attributes is completely optional. If you don't like them or they don't fit your application for any reason, you can keep injecting the dependencies and defining the services in the configuration file as before.
The blog post mentions the "factory" option of the AutowireInline attribute. However, the constructor of the attribute does not seem to have a parameter called "factory" (https://github.com/symfony/dependency-injection/blob/7.1/Attribute/AutowireInline.php).
I just played a bit with the new attribute and think that the examples should be changed to something like this ("args" should be renamed to "arguments" and the attribute does not have a parameter named "factory" but the factory config must be passed via the parameter "class"):
#[AutowireInline(class: SomeSourceAwareLogger::class, arguments: [new Reference(LoggerInterface::class), 'bar'])]
#[AutowireInline( class: [SomeSourceAwareLoggerFactory::class, 'staticCreate'], arguments: [new Reference(LoggerInterface::class), 'someParam'], )]