Skip to content

[DependencyInjection] Target parameter attribute does not work for Services #51565

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
issamkhadiri1989 opened this issue Sep 5, 2023 · 6 comments

Comments

@issamkhadiri1989
Copy link

Symfony version(s) affected

6.3

Description

Hello,

I am having an issue with the #[Target] attribute when using it in services.

According to https://symfony.com/doc/current/service_container/autowiring.html#dealing-with-multiple-implementations-of-the-same-type,

when I have 2 services implementing the same interface i have 2 choices:

  • create an alias from the Interface and use a default service and create a named alias for the other services

  • use the Target attribute.

With the first choice, everything works fine. But with the 2nd choice, I have created a default alias from the interface, then I create another alias to the other service. When I use the alias in some service I got an error

Cannot resolve argument $client of "App\Controller\TwitterController::index()": Cannot autowire service "App\Service\TwitterClient": "#[Target('appTransformerUpper')" on argument "$transformer" of method "__construct()"

Am I doing something wrong ?

How to reproduce

  1. Create an interface
interface TransformerInterface
{
    public function transform(string $input): string;
}
  1. Create 2 services that implement the same interface
class UppercaseTransformer implements TransformerInterface
{
    public function transform(string $input): string
    {
        return \strtoupper($input);
    }
}
class Rot13Transformer implements TransformerInterface
{
    public function transform(string $input): string
    {
        return \str_rot13($input);
    }
}
  1. Create an alias from the interface in services.yaml
App\Utils\TransformerInterface: '@App\Utils\Transformer\Rot13Transformer'

And then, create another alias to the other service (cf the doc)

app.transformer.upper: '@App\Utils\Transformer\UppercaseTransformer'

Use the Interface as a Type-hint in some service (eg. TwitterClient service) like the following

use Symfony\Component\DependencyInjection\Attribute\Target;

class TwitterClient
{
    private TransformerInterface $transformer;

    public function __construct(
        #[Target('app.transformer.upper')]
        TransformerInterface $transformer
    ) {
        $this->transformer = $transformer;
    }
 ...
}

Possible Solution

No response

Additional Context

No response

@HypeMC
Copy link
Contributor

HypeMC commented Sep 5, 2023

The #[Target] attribute doesn't work with service aliases, use the #[Autowire] attribute instead. See #50541.

@xabbuh
Copy link
Member

xabbuh commented Sep 6, 2023

closing as explained

@xabbuh xabbuh closed this as not planned Won't fix, can't repro, duplicate, stale Sep 6, 2023
@issamkhadiri1989
Copy link
Author

Thank you @HypeMC

It works !

I added a App\Utils\TransformerInterface $upperTransformer: '@App\Utils\Transformer\UppercaseTransformer' and then used #[Target('upperTransformer')].

However I should mention that in the doc, the statement

Another possibility is to use the #[Target] attribute. By using this attribute on the argument you want to autowire, you can define exactly which service to inject by using its alias.

could be confusing as there is no example explaining the idea.

Thank you again for the explanation

Best regards !

@stof
Copy link
Member

stof commented Sep 6, 2023

the Target attribute works with named aliases, allowing to keep the argument name itself whather you want (so not always matching your named alias)

@eerison
Copy link

eerison commented Sep 29, 2023

Thank you @HypeMC

It works !

I added a App\Utils\TransformerInterface $upperTransformer: '@App\Utils\Transformer\UppercaseTransformer' and then used #[Target('upperTransformer')].

However I should mention that in the doc, the statement

Another possibility is to use the #[Target] attribute. By using this attribute on the argument you want to autowire, you can define exactly which service to inject by using its alias.

could be confusing as there is no example explaining the idea.

Thank you again for the explanation

Best regards !

did you add this in your bind configs?

I do not know why the servers are not found in binds :/

@issamkhadiri1989
Copy link
Author

Hi @eerison

No i didn't have to add any bind config. You need to have a named alias and then you can report the name as Target's argument

javiereguiluz added a commit to symfony/symfony-docs that referenced this issue Jun 24, 2024
…(HypeMC)

This PR was merged into the 5.4 branch.

Discussion
----------

[DependencyInjection] Clarify the `#[Target]` attribute

The `#[Target]` attribute seems to be a constant source of confusion for developers, as evident by:

- symfony/symfony#50541
- symfony/symfony#51565
- symfony/symfony#54578

Also, the example given is either unclear or just wrong. Hopefully, this helps clarify things.

Commits
-------

2fb1ada [DependencyInjection] Clarify the `#[Target]` attribute
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants