MVC, DI (dependency injection) and instantiating a model from a controller

My dispatcher selects the correct controller; then create an instance of the controller (DependencyInjectionContainer is passed to the controller constructor); then calling some controller method ...

class UserController extends Controller { public function __construct(DependencyInjectionContainer $injection) { $this->container = $injection; } public function detailsAction() { ... } } 

DependencyInjectionContainer contains a DB adapter object, a Config object, etc. Now let's see what the detailsAction () method contains ...

 public function detailsAction() { $model = new UserModel(); $model->getDetails(12345); } 

As you can see, I am creating a new instance of UserModel and calling getDetails methods. The getDetails () method must connect to db to get user information. To connect to the database, UserModel must have access to the DB adapter.

What is the correct way to pass a DependencyInjectionContainer to a UserModel? I think this way is wrong ...

 public function detailsAction() { $model = new UserModel($this->container); $model->getDetails(12345); } 
+5
php dependency-injection model-view-controller
source share
2 answers

Instead of injecting the entire DI container into your classes, you should only enter dependent ones .

Your UserController requires a DB adapter (call this IDBAdapter interface). In C #, it might look like this:

 public class UserController { private readonly IDBAdapter db; public UserController(IDBAdapter db) { if (db == null) { throw new ArgumentNullException("db"); } this.db = db; } public void DetailsAction() { var model = new UserModel(this.db); model.GetDetails(12345); } } 

In this case, we introduce the dependency in the UserModel. In most cases, however, I would be inclined to regard it as a DI smell if the UserController only accepts the dependency in order to pass it, so a better approach might be for the UserController to depend on an abstract Factory like this:

 public interface IUserModelFactory { UserModel Create(); } 

In this embodiment, the UserController might look like this:

 public class UserController { private readonly IUserModelFactory factory; public UserController(IUserModelFactory factory) { if (factory == null) { throw new ArgumentNullException("factory"); } this.factory = factory; } public void DetailsAction() { var model = this.factory.Create(); model.GetDetails(12345); } } 

and you can define a specific UserModelFactory that takes a dependency on IDBAdapter:

 public class UserModelFactory : IUserModelFactory { private readonly IDBAdapter db; public UserModelFactory(IDBAdapter db) { if (db == null) { throw new ArgumentNullException("db"); } this.db = db; } public UserModel Create() { return new UserModel(this.db); } } 

This gives you a better separation of concerns .

If you need more than one dependency, you simply enter them through the constructor. When you start getting too much, this is a sign that you are violating the principle of single responsibility , and this is the time for refactoring for aggregated services .

+10
source share

I would use a singleton object for all configuration parameters: you configured it in your bootstrap, then decided to use it directly or pass it as a parameter in your objects.

The idea is to have one method to get your configuration data.

Then you can provide an abstract class for manipulating db that uses your configuration. singleton. DependancyInjection can still be used to override your default data.

The above link in the comment (possible "duplicate") ends when using the constructor injection: this is close to your current method.

However, if I try to understand how your model works, I think you will have many other model classes besides "userModel". Thus, an abstract class using a singleton configuration might be a good solution: all of your next model classes will simply extend this abstract class and you don’t have to worry about your configuration.

On the other hand, your solution is good for me, as long as your dependencyInjectionContainer changes often.

0
source share

All Articles