-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
DI child containers #29075
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
Comments
You can already have a sub-container that has access to only a subset of the main container with service locators. It's not exactly a child container since it only proxies a subset of services of the main container (but it can make publicly available services that are private in the main container), but I believe it can handle all use cases for child containers. |
@jvasseur Whilst that is interesting, it's very limited because each service has to be defined one-by-one in the service locator. That is highly restrictive compared to creating a real DI container that can be configured with glob resources to mass include all services matching a certain file pattern. |
@Bilge you can use glob resources or defaults in a specific file to add a tag to your services that you want to be in the sub-container then use a compile pass to collect all services with that tag and create a service locator with all those services. This is what is done in the framework for form types for example: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/DependencyInjection/FormPass.php#L63-L84. |
Thanks @jvasseur. I had presumed I would need to do as much but the code example will surely be helpful. I am not exactly sure which bits I need to replicate, however. It seems that code is probably doing a lot more than I would need to do. Do I just need to copy definitions from the container into the service locator? Documentation on this topic is quite scarce. |
|
There's also another way to create several containers: multi-kernel apps. That's basically what a kernel is: a scope for a container. Many apps use these since years. |
@nicolas-grekas That doesn't seem like a very intuitive solution, and poses more questions than it answers. Specifically, how would one get services in one kernel that depend on services in another kernel to work together and without being optimized away by the compiler? |
I'm still missing a use case here. Could you think of a real life scenario where you'd need a "sub-container"? I thought service locators cover our bases. |
@jakzal To my understanding, a service locator is a container with a subset of the parent container's services (that's not its standard definition, but is an intended use-case of service locators within the Symfony framework). Therefore, a service locator is a sub-container, and seems to be perfectly suited to my purposes. I'll try implementing it as described in this ticket tomorrow. However, even if it does work, it's worth noting that this use-case is pretty poorly documented at present. It does not seem easy or intuitive to find and use |
I was able to implement this successfully and didn't even need By tagging the glob services and adding a compiler pass to augment the Service Locator's constructor injection definition, together with It's unfortunate this cannot be done entirely in config, however, as it increases the complexity somewhat. Perhaps in future there might be support for referencing tagged services in the config. Thanks to all participants for your help and support. 👏 |
@Bilge if you're up for a doc PR, many will be able to follow your lead here :) |
@nicolas-grekas That would be interesting. Upon re-reading the doc, I now see there is a mention of
When I read that, I started to worry that not using the static $a = self::$container->get('my_locator');
$b = self::$container->get('my_locator');
self::assertSame($a, $b); Since this test passed I assumed I do not need to use Notwithstanding I don't know the RST format, like I said at the beginning, it's interesting because it seems you originally wrote that piece of documentation but I can't see how it would segue into my implementation because it seems diametrically opposed to what you're currently recommending to people wanting to implement Service Locators! Edit: After reading the I've only been using Symfony for a few weeks now and I don't fully understand the impact or drawbacks of my approach. Specifically, I notice one key difference is that you "memoize" all the service references in your implementation by wrapping them in |
I'm not sure to understand all the details here so feel free to share some code/example app. |
Symfony supports the concept of just a single DI container for the entire application. If the configuration file becomes too big it can be split into separate files with includes, but it does not support isolating separate groups of services in separate containers. This is true despite the fact that the documentation explains how to create containers from scratch with configuration files.
It may be desirable in some applications to provide sub-containers that contain services of just one type for scenarios where container injection is desirable or required, but one does not want to inject the main container with all services. This is already possible by defining a
ContainerInterface
service that loads a custom configuration file as shown in the aforementioned documentation section. However, even though only services defined in the sub-container can be retrieved from that container, some of the services in the sub-container may have dependencies on services in the main container. For this use-case, it is important to establish a parent/child relationship between the sub-container and the main Symfony container in which the child container defers to the parent for dependencies it cannot fulfil.Zend Framework already supports separate containers for different groupings of services, such as controllers and forms, but it's a little less trivial in Symfony due to the compiler steps. We cannot simply override
get()
to defer to the parent container for missing service definitions because compilation will fail before we can ever make aget()
call. The compiler must be made aware of the parent container and either copy in or link to the definitions it needs from the parent according to the dependency graph.The text was updated successfully, but these errors were encountered: