Search code examples
phpmodel-view-controllerdependency-injectioninstantiation

Composing a Controller class with Dependency Injection in PHP


How to solve the problem of composing a Controller class in PHP, which should be:

  • easily testable by employing Dependency Injection,
  • provide shared objects for end programmer
  • provide a way to load new user libraries

Look down, for controller instantiation with a Dependency injection framework


The problem is, that derived Controllers may use whatever resources the programmer wants to (eg. the framework provides). How to create a unified access to shared resources (DB, User, Storage, Cache, Helpers), user defined Classes or another libraries?

Elegant solution?

There are several possible solutions to my problem, but neither one looks to be a elegant

  • Try to pass all shared objects by constructor? (may create constructor even with 10 placeholders)
  • Create getters, settters? (bloated code) $controller->setApplication($app)
  • Apply singletons on shared resources? User::getInstance() or Database::getInstance()
  • Use Dependency Injection container as a singleton for object sharing inside the controller?
  • provide one global application singleton as a factory? (this one looks very used in php frameworks, hovewer it goes strongly against DI principles and Demeter's law)

I understand, that creating strongly coupled classes is discouraged and banished for :), however I don't know how this paradigm applies to a starting point for other programmers (a Controller class), in which they should be able to access shared resources provided to the MVC architecture. I believe, that breaking up the controller class into smaller classes would somehow destroy the practical meaning of MVC.


Dependency Injection Framework

DI Framework looks like a viable choice. However the problem still persists. A class like Controller does not reside in the Application layer, but in the RequestHandler/Response layer.

How should this layer instantiate the controller?

  • pass the DI injector into this layer?
  • DI Framework as a singleton?
  • put isolated DI framework config only for this layer and create separate DI injector instance?

Solution

  • Are you developing a framework yourself? If not, your question does not apply, because you have to choose from already existing frameworks and their existing solutions. In this case your question must be reformulated like "how do I do unit testing/dependency injection in framework X".

    If you are developing a framework on you own, you should check first how already existing ones approach this issue. And you must also elaborate your own requirements, and then just go with the simplest possible solution. Without requirements, your question is purely aesthetic and argumentative.

    In my humble opinion the simplest solution is to have public properties which initialize to defaults provided by your framework, otherwise you can inject your mocks here. (This equals to your getters/setters solution, but without the mentioned bloat. You do not always need getters and setters.) Optionally, if you really need it, you may provide a constructor to initialize those in one call (as you suggested).

    Singletons are an elegant solution, but again, you must ask yourself, is it applicable in your case? If you have to have different instances of the same type of object in your application, you can't go with it (e.g. if you wish to mock a class only in half of your app).

    Of course it is really awesome to have all the options. You can have getters/setter, constructors, and when initialization is omitted, default are taken from a singleton factory. But having too many options when not needed, is not awesome, it is disturbing as the programmer has to figure out which convention, option and pattern to use. I definitely do not want to make dozens of design decisions just to get a simple CRUD running.

    If you look at other frameworks you will see that there is no silver bullet. Often a single framework utilizes different techniques depending on the context. In controllers, DI is a really straightforward thing, look at CakePHP's $helpers, $components variables, which instruct to inject appropriate variables into the controller class. For the application itself a singleton is still a good thing, as there is always just a single application. Properties less often changed/mocked are injected utilizing public properties. In case of an MVC, subclassing is perfectly viable option as well: just as AppController, AppView, AppModel in CakePHP. They are inserted into the class hierarchy between the frameworks's and all your particular Controller, View and Model classes. This way you have a single point to declare globals for your main type of classes.

    In Java, because of dynamic class loaders and reflection, you have even much more options to choose from. But on the other hand, you have to support much more requirements as well: parallel requests, shared objects and states between worker threads, distributed app servers etc.

    You can only answer the question what is right for you, if you know what you need in the first place. But actually, why do you write just another new framework anyway?