Dutch77
Dutch77

Reputation: 1047

Symfony 4 - vendor bundle cannot find tagged autowired service

I made simple API bundle which is(will be) shared among the projects. Every object/entity has its own configuration that says which fields and how they're gonna be serialized(decomposed to scalar values). How they're gonna be serialized depends on selected normalizer. Right now there is only one default normalizer that recognize value as scalar, iterable or object. So far it works great. When you need some specific serialization like for DateTime (where you don't want decompose object to implicit scalar values, but set output format), you just make a new normalizer that process that object accordingly.

Since which normalizer to use is defined in configuration yaml file and can be passed from other sources, name of normalizer must be a string.

        App\Entity\Example\Category:
            normalizer: asyf.api.service.normalizer.default
            fields:
                title:
                    expose: true
                slug:
                    expose: false
                items:
                    orderBy:
                        title:
                            priority: 1
                            direction: DESC
                    limit: 2
                    expose: true

Normalizers are tagged as "asyf.api.normalizer". As this is created as vendor bundle I used it's dependency injection extension to load services by tag and inject them to NormalizersManager service that stores them and can revive them by key name

class AsyfApiExtension extends Extension
{
    /**
     * @param array $configs
     * @param ContainerBuilder $container
     *
     * @throws \Exception
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
        $loader->load('services.yml');

        $container->setParameter('asyf_api', $config);

        $normalizerManagerDefinition = $container->findDefinition('asyf.api.service.normalizers_manager');
        $defaultNormalizerId = 'asyf.api.service.normalizer.default';
        $defaultNormalizerReference = new Reference($defaultNormalizerId);
        $normalizerManagerDefinition->addMethodCall('add', array($defaultNormalizerId, $defaultNormalizerReference));

        $taggedNormalizers = $container->findTaggedServiceIds('asyf.api.normalizer');

        foreach ($taggedNormalizers as $id => $tags) {
            $normalizerReference = new Reference($id);
            $normalizerManagerDefinition->addMethodCall('add', array($id, $normalizerReference));
        }
    }
}

Now to the problem: This container in AsyfApiExtension::load cannot see tagged services outside its scope. It's just seeing services defined in this bundle. So when I create new autowired normalizer and register it in config/services.yaml with tag, vendor bundle doesn't see it and it's not added to NormalizersManager service, therefore it cannot be used.

    App\Service\Search\Normalizer\SearchResultNormalizer:
        public: true
        tags:
            - { name: 'asyf.api.normalizer' }

To solve this I can add it manually but then I loose this automatic configuration thought. Is there a way to make new autowired normalizers accessible in vendor extension load? Thx for any ideas

Upvotes: 1

Views: 609

Answers (1)

Dutch77
Dutch77

Reputation: 1047

I have found a solution, I have to rewrite a bit of my code but here it is:

    asyf.api.service.normalizers_manager:
        class: App\Service\Search\NormalizersManager
        arguments: [!tagged asyf.api.normalizer]

!tagged asyf.api.normalizer injects a collection of services with tag asyf.api.normalizer

Upvotes: 2

Related Questions