Question

Imagine 2 entities - User and Group. User is always in one group and a group has always one superuser. These entity classes only hold the data (no logic there).

Both entities have Mapper classes (handling SQL queries): UserMapper and GroupMapper. Both are dependent on each other when querying for it's entity:

  • UserMapper needs GroupMapper when querying for User to retrieve User's $group
  • GroupMapper needs UserMapper when querying for Group to retrieve Group's $superuser

I have been using Symfony's dependency injection component to inject dependencies in the constructor. But in this case I ran into trouble with this approach. I know I could use ORM like Doctrine which would handle this for me, but it is not an option at this time. What is the cleanest/best solution to this problem?

Here is an illustration of the scheme:

Problem illustration

Was it helpful?

Solution

One possibly approach for this is to introduce a third service SL and inject it, like this:

  • Mapper is an interface
  • GroupMapper implements Mapper, receives SL by injection
  • UserMapper implements Mapper, receives SL by injection
  • SL has methods:
    • function register($name, Mapper m)
    • function get($name) : Mapper
  • GroupMapper::__construct(SL $sl, ...) includes something like:
    • $this->sl = $sl;
    • $this->sl->register('group', $this);
  • UserMapper::__construct(SL $sl, ...) includes something like:
    • $this->sl = $sl;
    • $this->sl->register('user', $this)

Then, assuming $um is an instance of UserMapper obtained the DIC, and it needs to use the group mapper service: $um->sl->get('user') returns the group mapper.

In essence, what this boils down to is implementing a service locator, which is not really nice, but has the advantage that it is tiny, essentially being a filtered subset of the full DIC. This will work as long as you do not start using either service before all mutually dependent services have been initialized, which may not be a good fit for some situations.

Another possibility is to make your service ContainerAware and use it in the same, but IMHO this is worse, although a bit shorter, because you essentially allow use of anything from the DIC without any control. The pattern I suggest minimizes the "contact surface".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top