Skip to content
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

Document injecting a dictionary into a service via autowiring #154

Open
tacman opened this issue Dec 19, 2021 · 9 comments
Open

Document injecting a dictionary into a service via autowiring #154

tacman opened this issue Dec 19, 2021 · 9 comments
Assignees

Comments

@tacman
Copy link
Contributor

tacman commented Dec 19, 2021

You will be able to retreive it through the dictionaries collection service:

$dictionaries = $container->get(\Knp\DictionaryBundle\Dictionary\Collection::class);
$dictionary   = $dictionaries['my_dictionary'];

Is there a way to inject a dictionary into a service, like Workflows? That is, if I want to inject a specific workflow into a service

public function __construct(private Symfony\Component\Workflow\WorkflowInterface $projectStateMachine)

what I'm currently doing is injecting the whole collection and extracting the one I want

    private Dictionary $voteDictionary;

    public function __construct(
                                private Dictionary\Collection $dictionaryCollection,
)
    {
        $this->voteDictionary = $this->dictionaryCollection['vote'];

What I'd like to do is

public function __construct(private DictionaryInterface $voteDictionary)

That is, bin/console debug:container dictionary would also show how to inject a specific dictionary. I don't think this is possible now, but if someone can point me in the right direction as to how to do this, maybe I can add it. Thanks.

@PedroTroller
Copy link
Member

You can if you create your own dictionary and implement the Dictionary interface but no, there is no injection based on dictionary name... But it's a good idea, we have to migrate to Symfony 6. I will try to do it in a same time.

@tacman
Copy link
Contributor Author

tacman commented Feb 11, 2022

Documentation for auto-wiring by name is here:

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

I might be able to implement this, though I'm sure someone else can much faster. In particular, since this is a bundle and not an app, there's a different setup with the CompilerPass. I'm sure I can find another bundle that implements it, though (like Workflow).

Or @PedroTroller, do you think you can implement it? Although it's low priority, I think it makes for a better DX.

@tacman
Copy link
Contributor Author

tacman commented May 23, 2022

Looking into it a little more, it seem that we'd need to inject the Collection into the configuration and loop through each dictionary and bind it.

@tacman
Copy link
Contributor Author

tacman commented Aug 10, 2022

@PedroTroller Any chance you could look at this? Or point me toward an example of something similar?

@PedroTroller
Copy link
Member

@tacman

Sorry, it's on my todolist but at the moment I've only had time for a quick look, I've found how it works in several other bundles and it doesn't seem complicated. If you want to try it by yourself, here is this kind of mechanics in the MonologBundle for example.

https://github.com/symfony/monolog-bundle/blob/master/DependencyInjection/Compiler/LoggerChannelPass.php#L157

@tacman
Copy link
Contributor Author

tacman commented Nov 10, 2022

It'd be nice if this got in before Symfony 6.2 is released later this month. Not urgent, of course, but it'd be so nice to inject just the dictionary the class needs instead of the entire registry.

@tacman
Copy link
Contributor Author

tacman commented Sep 29, 2023

Every few months I come back to this issue to see if my Symfony skills have improved enough to solve this. Alas, still no, and I'm left with code like:

    private Dictionary $entityClassIconsDictionary;
    private Dictionary $coreIconsDictionary;

    public function __construct(
        private DictionaryCollection $dictionaries,
    ) {

        $this->entityClassIconsDictionary = $dictionaries['entity_class_icons'];
        $this->coreIconsDictionary = $dictionaries['core_icons'];
    }

instead of

    public function __construct(
        private Dictionary $coreIconsDictionary,
        private Dictionary $entityClassIconsDictionary,
    ) {

    }

I've looked at Monolog and other bundle (https://github.com/thephpleague/flysystem-bundle/blob/3.x/src/DependencyInjection/FlysystemExtension.php) but I still am stuck.

Can you help point me in the right direction? I think in the compiler pass, I need to find the Dictionary classes and make them public (currently, just the Collection class is public). If you could help me get the Dictionary classes from within the compiler pass, I might be able to figure out how to make them public and inject them.

Thanks.

@tacman
Copy link
Contributor Author

tacman commented Sep 29, 2023

Maybe we could start by making the classes that implement the Dictionary interface public.

bin/console debug:container knp_dictionary.dictionary.action_icons

 // This service is a private alias for the service                                                                     
 // knp_dictionary.dictionary.action_icons.8f4545fe939e19c735fc617c64dead50.traceable                                   

Information for Service "knp_dictionary.dictionary.action_icons.8f4545fe939e19c735fc617c64dead50.traceable"
===========================================================================================================

 ---------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
  Option           Value                                                                                                                                                             
 ---------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
  Service ID       knp_dictionary.dictionary.action_icons.8f4545fe939e19c735fc617c64dead50.traceable                                                                                 
  Class            Knp\DictionaryBundle\Dictionary\Traceable                                                                                                                         
  Tags             knp_dictionary.dictionary                                                                                                                                         
                   container.decorator (id: knp_dictionary.dictionary.action_icons, inner: knp_dictionary.dictionary.action_icons.8f4545fe939e19c735fc617c64dead50.traceable.inner)  
  Public           no                                                                                                                                                                
  Synthetic        no                                                                                                                                                                
  Lazy             no                                                                                                                                                                
  Shared           yes                                                                                                                                                               
  Abstract         no                                                                                                                                                                
  Autowired        no                                                                                                                                                                
  Autoconfigured   no                                                                                                                                                                
  Usages           knp_dictionary.dictionary.action_icons                                                                                                                            
                   Knp\DictionaryBundle\Dictionary\Collection                                                                                                                        
 ---------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------ 


 ! [NOTE] The "knp_dictionary.dictionary.action_icons" service or alias has been removed or inlined when the container  
 !        was compiled.                                                  

So maybe somewhere in DictionaryRegistrationPass.php?

    public function process(ContainerBuilder $container): void
    {
        foreach ($container->findTaggedServiceIds(self::TAG_DICTIONARY) as $id => $tags) {
            $container
                ->getDefinition(Collection::class)
                ->addMethodCall('add', [new Reference($id)])
            ;
        }
    }

So maybe get the reference by id and set that public? Sorry for a basic Symfony DI Services question, but this stuff is still kinda magic.

@PedroTroller
Copy link
Member

In fact, I've just had a look at it and it's a bit complicated at the moment. Logic would dictate that you could retrieve dictionaries by name (for example, a dictionary named postal_code would be injectable using Dictionary $postalCode), but that means you need to be able to retrieve the name of the dictionary. But this is currently the result of getName(), so you can't retrieve it in CompilerPass without creating a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants