Required and not required dependencies regarding DI-Container

Browsing this thread for me another question arose.

In the context of constructor or setter injection @TomB in his post differentiates between dependencies that are required or not required for the object’s functionality. His strategy sounds absolutely logical: Required dependencies must get injected via constructor, not required ones may be injected via setter.

Here’s an example:

class PersonModel {
  protected $personRepository;
  protected $paymentModel;

  public function __construct(PersonRepository $repository) {
    $this->personRepository = $repository;
  }

  public function injectPaymentModel(PaymentModel $model) {
    $this->paymentModel = $model;
  }

  public function getPerson($id) {
    return $this->personRepository->getEntityById($id);
  }

  public function getPaymentDataOfPerson(PersonEntity $entity) {
    if ($this->paymentModel) {
      return $this->paymentModel->getPaymentData($entity);
    }

    throw new Exception('no PaymentModel given');
  }
}

Now, my question is: How would you access this PersonModel in both cases …

  • case A: one doesn’t need the PaymentData
  • case B: one does need the PaymentData

… with the help of a DI-Container and this magical type hinting recognition as we know it from Laravel’s Service Container or Tom’s Dice.

Firstly, thanks for checking out Dice :slight_smile: However, I’m not sure I fully understand your question. What do you mean by “Access this PersonModel”

Access it from where?

However, and this may answer your question: since PaymentModel is only required in one method, it’s probably a lot simpler to inject PaymentModel into getPaymentDataOfPerson as an argument there, then you don’t need the if or the injectPaymentModel method.

Secondly, your class may be doing too much, which is why you have two different use-cases. Move getPaymentDataOfPerson into it’s own class, perhaps PersonPaymentData that has a dependency on PersonEntity and PaymentModel

Well this is an interesting topic. And I actually have another question related to this one. I see in your code, the user model accepts a repository in its constructor. Is it a good practice for the model to be aware of the repository’s presence? If not, how will you lazy load domain models inside other domain models(for instance, user model has a property that stores user profile model, which is lazy loaded since you dont want to load user profiles for all users across all pages)?

Yes, this answers my question related to this concrete example. My intention was rather how to realize the concept of required and not required dependencies using a DIC. My bad, the example doesn’t actually fit. I’ll try it again as follows :wink:

class A() requires an instance of class B to function and therefore ask for it in its __construct method via type hinting. It has another loose dependency on an instance of class C to provide it’s full functionality and gets this one injected by a setter method injectC().

A DI container now holds an instance of a class A that gets shared across the application. It got constructed with an instance of class B but the injectC() method was not called yet. At some point of the application we need the full functionality and therefore inject class C via injectC().

So far so good. But haven’t we now violated the concept behind a DI container? Or is it not a DIC’s job to handle “not required to function” dependencies? Or should I completely forget about such a loose variant of dependencies?

@Hall_of_Famer Your question preys on my mind, too! Especially, if the related object recursively lazy loads the initial object :flushed:

Interesting question. I think this is the kind of thing that’s very difficult to give an answer to without a real world example (as different examples will likely require different solutions). Generally speaking, in the situation you described you wouldn’t want to inject a dependency into an object that is shared between other objects.

For example, using your example consider that your A object has B set but has not had C set yet via injectC

We can now pass the A instance into classes X and Y which use the A object in some way. Let’s imagine a function useA() on the X object: $x->useA() which does something using the A class, and a similar method on Y, $y->useA(). If $y->useA() alters the state of A this is going to have a knock on effect on X which it shouldn’t.

This will be easier to follow with a full example:


class A {
	private $b;
	private $c;

	public function __construct(B $b) {
		$this->b = $b;
	}

	public function injectC(C $c) {
		$this->c = $c;
	}

	public function calculate() {
		//Do something different if 'C' is set:

		if ($this->c) return 1;
		else return 2;
	}
}


class B {
	
}


class C {
	
}



class X {
	private $a;

	public function __construct(A $) {
		$this->a = $a;
	}

	public function useA() {
		return $this->a->calculate();
	}
}


class Y {
	private $a;

	public function __construct(A $) {
		$this->a = $a;
	}

	public function useA() {
		//Inject C into A
		$this->a->inject(new C);
		return $this->a->calculate();
	}
}

Which we can then use, like so (ignoring a DIC)


$a = new A(new B);

$x = new X($a);

$y = new Y($a);

If we then call useA on X and Y we’ll see the following output:

echo $x->useA();
echo $y->useA();

Which obviously prints

1
2

However, if we alter the order of method calls so Y is used first:

echo $y->useA();
echo $x->useA();

we get:

2
2

Rather than reversing the order we’ve actually affected the result in a way that we probably didn’t want to. This is often called action at a distance and is a classic way of introducing bugs (consider how easy it would be to accidentally alter the order of events in a system where we have these inside various other function calls)

This is a problem with shared states, of course and the best way to fix this, is instead of sharing the same A instance, we give each object it’s own instance configured how it needs to be for that operation:


$a1 = new A(new B);
$x = new X($a1);

$a2 = new A(new B);
$a2->injectC(new C);

$y = new Y($a2);

Now, whichever order we call the methods in:

echo $y->useA();
echo $x->useA();

or

echo $x->useA();
echo $y->useA();

We get the same, expected result.

Again, this is very theoretical in there are some instances where the former behaviour (allowing the order of events to alter the outcome) is desirable, but in most cases it’s not.

When this is not, you actually know when X or Y is created whether to inject C or not, and the DIC can be configured to do so.

It’s really a completely different topic but Doctrine 2 implements lazy loading by using proxy objects. So when you call $user->getProfile() you are actually talking to a user proxy object which checks to see if a profile was already loaded and if not queries the database for one. The application thinks you have user objects when in fact you have something more complicated.

Lazy loading is fun when first encountered. But, IMHO, it’s best to simply load what the request needs and not deal with the magic.

The Decorator Pattern is probs all your need in most cases:

I see, its interesting idea for proxy object, but the question is, how does the proxy object know how to load its data? I’d assume it will need to talk to database, via a database object such as MySQLi and PDO. And such databaseobjects will have to be passed to each proxy object’s constructor via dependency injection, so they can use database object to query the missing/lazy-loaded data when asked.

So in a way it’s not much different from storing data mappers or repository objects inside domain models, as these proxy objects also contain data access logic. With the data mapper/repository approach, I can just lazy load data by using the data mapper/repository injected into the domain model(likely stored as a property), it does the same thing as proxy. What’s the benefit of using proxy then, as compared to just injecting data mappers/repositories to domain model?

Consider taking Doctrine 2’s ORM out for a test drive. Much easier to understand what is happening. The basic idea is that Doctrine generates these proxies automatically for you. So all the magic occurs behind the scenes. As far as your application is concerned, the entities really are just plain “pure” domain php objects.

The proxy objects are cached as php files so once you establish the mapping between your entity and the database then it’s easy enough to see all the generated code.

So you are saying that with proxy classes, the domain model no longer has data access logic, its moved to the proxy objects and totally hidden from the model?

yep

It is a short but interesting read.

http://doctrine-orm.readthedocs.org/en/latest/reference/advanced-configuration.html#proxy-objects

Scott

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.