Symfony2 service structure - php

I am having a hard time understanding Symfony2 services. I have read lots of stuff everywhere (including some here in SO) but none seems to fully explain it.
Suppose I have a bundle A and a separated bundle B. I want B functionality available to the A bundle. I want to inject B in the service container so A will be able to use it.
Which bundle should have a Services directory? Which one should have a configuration file? Both if needed? And where the Extension goes? Why?

Bundle B will require an Extension in order to load it's services.xml file.
Bundle B will require an entry in it's services.xml file to define the service.
Bundle B will have the Services directory containing your service class which exposes the desired functionality.
Bundle A does not require anything special. It will be able to use the container to access the service exposed by Bundle B. Just needs to know the service id.
It's confusing until you make a few services.

Read these two questions and my answers to them first:
Symfony2 conceptual issue: general bundles vs. specific ones,
Should everything really be a bundle in Symfony 2?
Assuming you're talking about app specific bundles, I suggest having one bundle only and keep services out it. Then, you could register your services in several ways:
Directly in the config.yml,
Creating an extension class in your AppBundle, or
Via annotations from JMSDiExtraBundle — this is what I prefer personally.

Related

Symfony bundle configuration

I've just started working with Symfony and have run into a problem that I'm having a hard time tracking down information about.
I'm trying to create a bundle which has its own configuration file, e.g. configuration for doctrine connections.
All documentation I've found have never mentioned of showed how this can be set. Is it possible?
What I want to solve:
I have a bundle which when installed should handle connection to a secondary database table without any configuration needed from the main application in which the bundle has been integrated. Ultimately the configuration in the bundle should be override-able from the main application.
The bundle should be in the lack for a better work "self contained".
I've found documenation about bundle configuration. But all I've seen mentioned there is if one would like to configure the bundle and not interaction with other components (might have missed something).
tl;dr I want to have a config (e.g. AppBundle/Resources/Config/config.yml) file inside a bundle which can configure things like doctrine.
What i've tried
I've tried placing the configuration inside a config.yml file located in Resources/Config/. But I guess the file is never read.
I think it is not good idea to put something related to configuration right inside your bundle and ruin it's reusability by doing such thing. As far as I understood your task what your really need is to configure second entity manager to manage entities from secondary database when you need them. Same problem and its solution are described in following question: Doctrine 2 - Multiple databases configuration and use
Hope that will help!

Symfony 2/3 Get Classes in Directory that Implement an Interface

Is there a nice way in Symfony 2 or 3 to load all classes within a directory that implements a particular interface?
Since Symfony 3.3/3.4 it is possible by using configuration only (without a need to write custom CompilerPass):
# config/services.yaml
services:
# ...
_instanceof:
App\HandlerInterface:
tags: ['app.handler']
App\HandlerCollection:
# inject all services tagged with app.handler as first argument
arguments: [!tagged app.handler]
and if you need to restrict services to register from a single directory see importing with resource
references:
https://symfony.com/doc/current/service_container/3.3-di-changes.html#auto-configure-with-instanceof
https://symfony.com/doc/3.4/service_container/tags.html#reference-tagged-services
http://symfony.com/doc/3.4/service_container.html#importing-many-services-at-once-with-resource
Short answer is: you can't.
You don't know, what is in a file until you load it.
Long answer (taking into account what you have wrote in the comment under the question):
The only thing you know before you load a file is its name. So one of solution is to name your modules' classes (and files) with a fixed pattern like UserModule, ProductModule and so on. That way you can load all modules by their names. But this is the solution that I wouldn't suggest.
I my opinion you should change the approach and inverse the workflow. Create a class in which you will define all modules that need to be loaded. In Symfony it's called by default AppKernel, in which you define bundles (modules) to be loaded and initialized.
This has a few advantages.
You can have multiple entry points to your application and configure each one with different modules.
You may have a few different environments (like production and development) with different modules loaded in both of them. (e.g. add some modules in development like profiler)
Also dependency managment is much easier, since you can load defined modules and add their dependencies also with autoloading.
In general I think that you should avoid manual loading any php files (except autoload.php or similar that contains autoloaders) at all.

Global logging service

I have written my own logging class that I defined as a service and placed inside the AppBundle\Services namespace. I can access it easily inside the controller when I want to log something, but what about accessing it from other services?
I'd have to pass the logging service as a dependency injection, but what if I have more than 100 services defined (services, modules, event listeners etc. etc.), each of them having their own dependencies? It would create a mess.
I've been also thinking about extending some core service that defines the logging service, but then again - all my services, modules, event listeners, would have to extend one core class.
What's the best approach to solve this?
May be a good approach would be to rethink responsibilities in order to avoid the creation of too many services.
About the fact of consumming service itself, I think there are no problem to reuse as much as you need along the application lifecycle. In fact, symfony will handle the instantiation and you will be only consumming it as a service.
Another approach will be to create base classes for all your core objects, let these base classes to handle log services and final classes will log in a implicit way. This will not save service calls but almost will leave you to handle it manually.
If you think you will use your log feature in several projects then I recommend you to move to vendors folder and use it as external module, synchronised via composer to your github account. It will be like your own 3rd party product.
If you are not a symfony friend then you can break the law and create your own singleton pattern available by autoloading, but I think you should take advantage of the powerfull symfony service structure.

Common lib on symfony2

Before using Symfony2, I used to have a common lib with a lot of simple but useful functions (for instance, a function which takes "Azè_rtï" in argument and returns "aze-rti").
So, well, I created a Bundle: CommonLibsBundle.
But.. I have only one or two php files. It does not make sense to me to use a controller / view / model in this kind of situation.
What should I do? May I erase all folders in my new bundle (Controller, DependencyInjection, Resources, Tests... + CommonLibsBundle.php) and just put my lib.php in it?
Many thanks,
Bliss
Unless you need to tap into the Symfony framework itself - for configuration or to define services, it doesn't need to be a bundle - it's just a library. Give it a reasonable namespace, and call as required as you would any other component or library.
Even if you wanted to add Symfony-specific services that you could call, there is something to be said to still have an external simple library - usable anywhere, which then is wrapped by a very thin bundle which would only add the Symfony-specific (or Laravel, or ZF, or whatever) services and configuration as required.

Extending of Symfony container

is it possible overwrite/extend Symfony\Component\DependencyInjection\Container::get() method? I want automatic creating service, when it is not contain in container, but class of service exists.
For example:
Name of service is My.MyBundle.Model.FooRepository
Service with this name doesnt exists, but when i call:
$container->get('My.MyBundle.Model.FooRepository');
check class_exists for \My\MyBundle\Model\FooRepository and when its exists, add to container and return it. Dependencies of this new services will be resolve by kutny/autowiring-bundle.
This feature can be extended only for some namespaces or interfaces and in production enviroment can be cached, but for developing will be great helper.
Any idea?
This is not directly answering your question but maybe it's answering your need: if you want to have "auto-wiring" inside your Symfony project, you can use PHP-DI inside Symfony. PHP-DI is an alternative container that can do auto-wiring (which Symfony does not).
Have a look at the Symfony 2 integration documentation to see if it can fit your bill.

Categories