Reputation: 17166
This question is not strictly related to Symfony 2, but as I use Symfony 2 components and will later likely use a Symfony\Component\DependencyInjection\Container as DI-Container, it might be relevant.
I am currently building a small library using components from Symfony 2, e.g. HttpFoundation, Validator, Yaml. My Domain Services are all extending a basic AbstractService providing nothing but Doctrine\ORM\EntityManager and Symfony\Component\Validator\Validator via Constructor-Injection like this:
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;
}
}
A Service-class extending this AbstractService may now need to inject additonal components, like Symfony\Component\HttpFoundation\Session. As of 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 without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
As I see it, when I use Setter-Injection for Session, I have to add checks before accessing it in my methods, whether it is already injected, which I want to avoid. On the other hand I don't want to "repeat" injecting the basic components shared by all services.
Upvotes: 10
Views: 4712
Reputation: 44851
I went through the same problem while developing my Abstract Controller Bundle — when controllers are defined as services — and ended up using the setter injection in the base class.
Upvotes: 0
Reputation: 317217
An alternative would be not to extend your Services from the abstract type. Change the abstract type to a real class and then inject it to your Services, e.g.
class MyService
…
public function __construct(ServiceHelper $serviceHelper)
{
$this->serviceHelper = $serviceHelper;
}
}
Yet another option would be to not pass the dependencies until they are needed, e.g.
class MyService
…
public function somethingRequiringEntityManager($entityManager)
{
// do something with EntityManager and members of MyService
}
}
Upvotes: 3
Reputation: 18859
Is there a more elegant way to solve this without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
Well, by using setter-injection like you've mentioned. Apart from that I see two possible solutions to your immediate problem:
Make the parent constructor accept a Session object as well, but make it null by default. This to me is ugly, since the parent doesn't actually need the session object, and if you have other implementations, this will result in a long parameter list for the parent constructor, so this is actually not really an option.
Pass in a more abstract object. Be it a ParameterObject for that specific type of object, or just a simple Registry, this should work. Again; not preferable and as you're using dependency injection, you probably already know why.
I do have to ask though; where's the harm in using your current way? I don't actually see the downside. I'd just go ahead and carry on. If you discover you're using even more parameters in the constructor, think about what the service's intentions are and why it needs it, chances are that particular pieces of behaviour can be moved to the objects you're requesting instead.
Upvotes: 1
Reputation: 1965
Well you could use singleton for the Session class and access it's instance anywhere, but that might not be an option if you want to restrict it's usage.
But then again Session::getInstance()->getStuff
seem's pretty much the same as $this->session->getStuff
so to answer your question - I don't think there's much of a choice here and you approach seems fine.
Oh and like Gordon said: you shouldn't change the order of the arguments.
Upvotes: 0