Constructor injection with class expansion

This question is not strictly related to Symfony 2, but since I use Symfony 2 components and most likely will use Symfony \ Component \ DependencyInjection \ Container as a DI container, this may be relevant.

I am currently creating a small library using components from Symfony 2, for example. HttpFoundation, Validator, Yaml. My domain services extend the basic function of AbstractService, providing nothing but Doctrine \ ORM \ EntityManager and Symfony \ Component \ Validator \ Validator through constructor injection as follows:

abstract class AbstractService { protected $em; protected $validator; /** * @param Doctrine\ORM\EntityManager $em * @param Symfony\Component\Validator\Validator $validator */ public function __construct(EntityManager $em, Validator $validator) { $this->em = $em; $this->validator = $validator; } } 

The Service class extending this AbstractService may now be required to add additional components, such as Symfony \ Component \ HttpFoundation \ Session. I do it like this:

 class MyService extends AbstractService { /** * @var Symfony\Component\HttpFoundation\Session */ protected $session; /** * @param Symfony\Component\HttpFoundation\Session $session * @param Doctrine\ORM\EntityManager $em * @param Symfony\Component\Validator\Validator $validator */ public function __construct(Session $session, EntityManager $em, Validator $validator) { parent::__construct($em, $validator); $this->session = $session; } } 

Is there a more elegant way to solve this problem without repeating the arguments of the parent constructor, for example. using Setter-Injection for Session instead?

As I see it, when I use Setter-Injection for Session, I have to add checks before accessing them in my methods, regardless of whether it has already been entered and what I want to avoid. On the other hand, I do not want to β€œrepeat” the injection of the main components shared by all services.

+4
source share
4 answers

An alternative might not be extending your services from an abstract type. Change the abstract type to a real class, and then enter it into your services, for example.

 class MyService … public function __construct(ServiceHelper $serviceHelper) { $this->serviceHelper = $serviceHelper; } } 

Another option is to not pass dependencies until they are needed, for example.

 class MyService … public function somethingRequiringEntityManager($entityManager) { // do something with EntityManager and members of MyService } } 
+3
source

Is there a more elegant way to solve this problem without repeating the arguments of the parent constructor, for example. using Setter-Injection for Session instead?

Good, using recycling, as you mentioned. In addition, I see two possible solutions to the problem immediately :

  • Make the parent constructor also accept the Session object, but by default it will be null. This is ugly for me, since the parent does not actually need a session object, and if you have other implementations, this will lead to a long list of parameters for the parent constructor, so this is actually not an option.

  • Go to a more abstract object. Whether it's the ParameterObject parameter for this particular type of object or just a simple Registry, this should work. Again; not preferable, and since you are using dependency injection, you probably already know why.

I need to ask; Where is the harm in using your current path? In fact, I do not see a flaw. I would go on and on. If you find that you are using even more parameters in the constructor, think about what the intent of the service is and why it is necessary, it is possible that some behavior elements may be transferred to the objects that you request.

+1
source

Well, you can use singleton for the Session class and access the instance anywhere, but this may not be an option if you want to limit its use. But again, Session::getInstance()->getStuff looks something like $this->session->getStuff , so to answer your question I don’t think there is a lot of choice here and you are getting closer, it seems , perfectly.

Oh, and, as Gordon said, you should not change the order of the arguments.

0
source

I ran into the same problem when developing the Abstract Controller Bundle - when the controllers are defined as services - and ended up using the setter insert in the base class .

0
source

Source: https://habr.com/ru/post/1395213/


All Articles