December 20, 2014
by Jason Roman





Categories:
Protips

Tags:
PHP  Symfony


Symfony Setter Injection and Parent Class Dependencies

Dealing with dependency absence in child class constructors.

In a previous post, I discussed cases where you might want to use setter injection - namely, using parent/child services. When doing so, another issue occurs that must be dealt with. The issue is that you no longer have access to the parent class dependencies in the constructor of your child class. I came across this when trying to set a class-level Doctrine repository.

class BaseClass
{
protected $em;

/**
* @param EntityManager $em
*/
public function setEntityManager(EntityManager $em)
{
$this->em = $em;
}
}

class ChildClass extends BaseClass
{
private $erOrder;

public function __construct()
{
$this->erOrder = $this->em->getRepository('MyBundle:Order');
}
}
This no longer works when using setter injection because constructors are *always* executed first. Even though Symfony2 will automatically call the setters of your parent class, they will not be called until after the constructor. Until then you simply don't have access to them.

So what is the solution? It's actually pretty simple: you just need to add another function to the child class that will be called automatically via setter injection.
// services.yml
services:
child_service:
parent: base_service
class: My\Bundle\Service\ChildService
calls:
- [setRepositories]
Now let's take a look at our child class after this small modification:
class ChildClass extends BaseClass
{
private $erOrder;

public function setRepositories()
{
$this->erOrder = $this->em->getRepository('MyBundle:Order');
}
}
This works because Symfony2 will call all of the setters on the parent before calling the setters on the child, so by the time you reach the child setters you will have access to all of the parent's dependencies. To recap the order of operations:

  • Child constructor
  • Parent setter injection calls
  • Child setter injection calls

Note that you don't have to prefix every function name with set like I am doing above; you could just as easily change setRepositories() to initialize().

Symfony2 provides many options for defining your services, so don't be afraid to experiment and discover what works best for you. These techniques worked for me. Talking about setter injection required two posts and what some would consider 'workarounds', so you might find other methods more suitable.