Symfony 2.8 Autowiring for method injection - php

Update 2020 (this should have been much sooner). There can be a new class for each controller action. Each controller can be named by the action it is going to do, with an Invoke() method. Consider "Action Domain Responder" (ADR).
Why do I want to do this? Controllers do not necessarily adhere to SRP, and I'm not about to go creating a new class for each of, what is effectively, a controller 'action'. Therefore, a controller should not have everything injected via constructor, but the relevant method being called should be able to state explicitly "I require these objects" and another method "these other objects".
As of Symfony 2.8, the Dependency Injection component now provides auto-wiring for your services if you provide autowire: true in services.yml.
I'm defining my controller as a service, like so:
test_controller:
class: AppBundle\Controller\TestController
autowire: true
My controller looks as follows:
class TestController
{
public function indexAction(TestClass1 $tc1, $id)
{
return new Response('The slug is: ' . $id);
}
}
You will notice I'm typehinting for TestClass, which is just an empty class. However, the following error appears when I refresh the page:
What do I need to change in my services.yml file to have auto wiring dependency injection in my controller method? Just a note, the issue isn't because I have an $id 'slug' afterwards. Removing it does nothing.

Edit: I've created a bundle allowing to do more or less what you want called DunglasActionBundle.
Disclaimer: I'm the author of the Symfony autowiring system.
The Symfony Dependency Injection Component (and the autowiring system is part of it) doesn't work that way. It only allows to automatically inject dependencies in the class constructor of services and knows nothing about controller classes, actions and other parts of the HttpKernel Component.
It's not currently possible to do what you want to do. Initially, the autowiring system has been designed for domain services, not for controllers.
It should be possible to bridge the autowiring system with controller parameters using a custom param converter. However, I'll suggest you take another way I've first described here:
There is another approach I want to discuss since several time but I
did not have the time to blog about it.
It's a derivate of/similar to the ADR pattern, applied to Symfony.
An action is a single class with a __invoke() method containing all it's
logic (should be only some lines of glue code to call the domain and
the responder).
Actions are registered as a service (it can be
automated using a compiler pass and some conventions, similar to how
we discover controllers right now, with the ability to override the
definition if needed)
On such action services, autowiring is enabled.
It means that almost as easy as current controllers to use for the
developper, but with an explicit dependency graph and a better
reusability. Only things that are really necessary are injected. In
fact, it's already doable to use such system with Symfony. We do it in
API Platform master. Here is one action for instance:
https://github.com/dunglas/DunglasApiBundle/blob/master/Action/PostCollectionAction.php
In this implementation I also rely a lof on kernel events to be able
to plug and play some sort of logic (persistence, validation...) in a
decoupled manner but it's out of scope.

Related

PHP MVC: Too many dependencies in controller?

I'm working on a personal HMVC project:
No service locators, no global state (like static or global), no singletons.
The model handling is encapsulated in services (service = domain objects + repositories + data mappers).
All controllers extend an abstract controller.
All project dependencies are injected through Auryn dependency injection container.
All needed dependencies are injected in the constructor of the abstract controller. If I want to override this constructor, then I have to pass all these dependencies in the child controller's constructor too.
class UsersController extends AbstractController {
private $authentication;
public function __construct(
Config $config
, Request $request
, Session $session
, View $view
, Response $response
, Logger $logger
, Authentication $authentication // Domain model service
) {
parent::__construct(/* All dependencies except authentication service */);
$this->authentication = $authentication;
}
// Id passed by routing.
public function authenticateUser($id) {
// Use the authentication service...
}
}
The dependencies list would further grow. This needs to change. So I was thinking about:
Totally separate controllers from views.
They would then share the service layer. The views wouldn't belong to controllers anymore and the Response would be a dependency of the views.
Use setter injection in controllersLike for Request, Session, Logger, etc;
Inject dependencies in the controller actionsOnly when needed.Like for Request, Session, Logger, etc;
Use decorator pattern.Like for logging after an action call.
Implement some factories.
To constructor inject only the needed dependencies only on child controllers.So not in AbstractController anymore.
I'm trying to find an elegant way to deal with this task and I'll appreciate any suggestions. Thank you.
I'll answer to my own question. When I wrote it I already had a good overview of what many experienced developers recommend regarding dependency injection in MVC and MVC structure.
The constructor injection is the proper option. But it seemed to
me that, following this line, I'll end up with too many
dependencies/arguments in constructors. Therefore giving controllers
too many responsibilities (reading the requested values, changing the
state of the domain objects, logging operations, requesting the view
to load the templates and to render the data, etc).
Setter injection was also a solution to take in consideration. But, in the course of the developing time on my project, I realised
that this solution really didn't fit (at least) into the picture of
my controller-view relationships.
The injection of the dependencies directly into the controller
actions caused me hard-time (but great time) too, having in mind
that I already had the url values injected as action arguments and
that I didn't used any routing dispatchers.
Implementing factories was also a great idea, in order to be able
to have objects at my disposal within each controller action.
Factories are a great tool to use, but only going from the premise of
needed run-time objects, not of just reducing the number of
dependencies in constructors.
Decorator pattern is also a good alternative. But if, for example, you want to log something within a controller action, then
this is not a solution: you still have to pass a logger as dependency
(in constructor, setter or action).
I had a thought about injecting the needed dependencies only on
child controllers. But then the problem of multiple
responsibilities of the corresponding controllers remains the same.
So, none of this solutions seemed to entirely fit into the structure of my HMVC project, whatever I did. So, i dug further, until I realised what the missing link was. For this I give my whole appreciation to Tom Butler, the creator of the following great articles:
Model-View-Confusion part 1: The View gets its own data from the
Model
Model-View-Confusion part 2: MVC models are not domain
models
His works are based on an in-depth, well argumented analyse of the MVC concepts. They are not only very easy to follow, but also sustained by self-explanatory examples. In a word: a wonderful contribution to the MVC and developer community.
What I'll write further is meant to be just a presentation of his principles with my own words, having in mind to somehow complete them, to provide a compacter perspective of them and to show the steps followed by me when I implemented them in my project. All credit on the subject, ideas and principles and workflows depicted here goes to Tom Butler.
So, what was the missing link in my HMVC project? It's named SEPARATION OF CONCERNS.
For simplicity I'll try to explain this by referring myself to only one controller, one controller action, one view, one model (domain object) and one template (file), introducing them into a User context.
The MVC concept most described on web - and also implemented by some popular frameworks that I studied - is centered around the principle of giving the controller the control over the view and the model. In order to display something on screen you'll have to tell that to the controller - he further notifies the view to load and render the template. If this display process implies the use of some model data too, then the controller manipulates the model too.
In a classical way, the controller creation and action calling process involve two steps:
Create the controller - passing it all dependencies, view inclusive;
Call the controller action.
The code:
$controller = new UserController(/* Controller dependencies */);
$controller->{action}(/* Action dependencies */);
This implies, that the controller is responsible for, well, everything. So, no wonder why a controller must be injected with so many dependencies.
But should the controller be involved in, or responsible for effectively displaying any kind of information on the screen? No. This should be the responsibility of the view. In order to achieve this let's begin separating the view from the controller - going from the premise of not needing any model yet. The involved steps would be:
Define an output method for displaying information on screen in the
view.
Create the controller - passing it all dependencies, excepting the
view and its related dependencies (response, template object, etc).
Create the view - passing it the corresponding dependencies
(response, template object, etc).
Call the controller action.
Call the output method of the view:
And the code:
class UserView {
//....
// Display information on screen.
public function output () {
return $this
->load('<template-name>')
->render(array(<data-to-display>))
;
}
//....
}
$controller = new UserController(/* (less) controller dependencies */);
$view = new UserView(/* View dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
By accomplishing the five upper steps we managed to completely decouple the controller from the view.
But, there is an aspect, that we hypothesised earlier: we did not made use of any model. So what's the role of the controller in this constellation then? The answer is: none. The controller should exist only as a middlemann between some storage place (database, file system, etc) and the view. Otherwise, e.g. only to output some information in a certain format on screen, is the output method of the view fully sufficient.
Things change if a model is beeing brought on the scene. But where should it be injected? In the controller or in the view? In both. They share the same model instance. In this moment the controller gains the role of the middleman - between storage and view - in his own right. A theoretical form would be:
$model = new UserModel;
$controller = new UserController($model, /* Other controller dependencies */);
$view = new UserView($model, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
Doing so, the controller can change the state of the model and ensure that it's saved in the storage system. The same model instance, respective its state, is read by the view and displayed. The controller passes display logic informations to the view through the model. The problem is, that these informations don't belong to the business logic, which a model is supposed to exclusively have. They are only display logic participants.
In order to avoid giving display logic responsibilities to the model, we have to introduce a new component in the picture: the view-model. Instead of sharing a model object, the controller and the view will share a view-model instance. Only this one will receive the model as dependency. An implementation:
$model = new UserModel;
$viewModel = new UserViewModel($model, /* Other view-model dependencies */);
$controller = new UserController($viewModel /* Other controller dependencies */);
$view = new UserView($viewModel, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
And the workflow can be described like this:
The request values are sent by the browser ("the user") to the
controller.
The controller stores them in the view-model instance as properties
(data members), therefore changing the display logic state of the
view-model.
Within its output method, the view reads the values from the
view-model and requests the model to query the storage on their
basis.
The model runs the corresponding query and passes the results back to
the view.
The view reads and passes them to the corresponding template.
After template rendering, the results are displayed on the screen.
The view-model does not belong to the domain model, where all domain objects reside and the real business logic takes place. It does also not belong to the service layer, which manipulates the domain objects, repositories and data mappers. It belongs to the application model, e.g to the place where the application logic takes place. The view-model obtains the sole responsibility of gaining display logic state from the controller and conducting it to the controller.
As can be seen, only the view-model "touches" the model. Both, the controller and the view were not only completely decoupled from each other, but also from the model. The most important aspect of this approach is, that each of all the components involved gains only the responsibilities, that it is supposed to gain.
By making use of this kind of component separation and of a dependency injection container, the problem of too many dependencies in controllers vanishes. And one can apply a combination of all options presented in my question in a really flexible manner. Without having in mind that one of the components (model, view or controller) gains too many responsibilities.

How to get ServiceManager in an abstract class

I writing an application with Zend Framework 2, and Doctrine 2. I have 2 month experience with both technologies.
I using Doctrine repository documents scheme. I need to access current user in repository class. I can't figure out how to get serviceManager instance in repository. I read lot of articles, but still can't understand dependency injection, I need to to manage service in ZF2.
Can someone explain me how to inject service to an abstract class (like base repository)?
You need to implement ServiceLocatorAwareInterface to lazy retrieve the serviceManager.
Quote from ZF2 documents:
Additionally, there are some cases where you may want to receive the ServiceManager to lazy-retrieve dependencies; as such, you may want to implement ServiceLocatorAwareInterface and know more details about the API of the ServiceManager.
See ZF2 Quick Start - service manager.
But to be honest you should not use any services within your repository classes. Keep them simple and clean just to retrieve data.
And when you call your repository class, you should pass the user like so:
$data = $this->getEntityManager()->getRepository('Common\Entity\User')->myMethod($user);

Is there a good way to keep the number of parameters (dependencies) passed to a controller down?

So in Silex, I am attempting to get the $app parameter out of my controller actions and just pass in the dependencies that are relevant to the controller/action.
Generally my controllers have these shared dependencies:
Template engine
Session provider
Translation provider
URL generator
Validation provider
I've got a base Controller class to accept these on the constructor by default. The controllers inheriting from it also end up taking those parameters as well. Additionally they take other parameters, such as an object repository/store (such as a user repository in an authentication or user controller).
I end up registering the controllers in Silex with at least 5 parameters to the constructor when it seems like I don't see a lot of that in other applications. Feels like I'm doing it wrong.
I also realize that I'm complaining about the same thing that the Pimple DI container "fixes," but using it also seems not quite true to the DI philosophy.
My question: Is there a better way to do what I'm doing?
Yes there are alternative solutions.
The first one is to "group" your parameters into new classes like ControllerUtil or things of the sort. That's not always easy or possible though, so I'm not a particular fan of that.
Benjamin Eberlei explains this approach in this blog post: Extending Symfony2: Controller Utilities
The second is to use alternative to constructor injection, like property or setter injection. This is not as clean as constructor injection, but it is extremely practical.
Given controllers are generally not the kind of components you want to reinject, reuse or unit-test a lot, so this is an acceptable compromise IMO. I've been using that method for about a year and I can't go back.
Here is a related blog post I wrote on that subject: Controllers as services?

DI Container and controllers

I just followed the http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 articles, and have some questions about the DI container.
Let's say I want to fire an event inside my controller, how would i get the dispatcher inside my controller?
I'm starting my test framework through
$c->get('app')->handle($request);
where 'app' is the Symfony\HttpKernel. How can i set the dependencies to the container? Let's say I have a view engine, defined in the container
$c->register('view.engine', 'Core\ViewEngine');
and I want to give that object, or resolve that object, inside my Controller to render some views. It's the same problem with the event fire, I don't have access to those values inside my controller ... How is a DI container supposed to work in situations like this?
Thanks!
There are different approaches. You might want to read through the silex documentation as a next step. In silex, the application itself is a DI container. You might also read through the introduction to Symfony 2 documentation.
The most straight forward approach (and the one used by S2 as a default) is to inject the DI container itself into your controller. The controller can then pull out services such as the dispatcher as needed.
A "better" approach is to inject the dispatcher along with whatever else the controller needs directly into the controller. It's "better" because the controller itself does not need access to the container. But it's more difficult since a controller often needs a number of services just to it's job.
==============================================
How would I inject the container in the controller though?
That is where looking at existing frameworks starts to come in handy. Remember that HTTPKernel is a component and not a framework. How you use it is up to you.
In Symfony 2 the app object is actually derived from Kernel and not HTTPKernel. The Kernel in turn contains an instance of HTTPKernel as well as an instance of the container.
There are several approaches you might take. There is no single "correct" one.
If you look into HTTPKernel::handleRaw you will find:
$controller = $this->resolver->getController($request))
You might make your own controller resolver object which would inject the container after creating the controller. Just one possibility.

Passing Entity Manager to Service Layer ZF Doctrine2

I'm trying to pass the entity manager to a service but havent find a correct way yet. I want to complete remove the em from the controller so thats why I'm finding another way.
I was thinking of this options:
1. I could save it in the registry and then try to access it from the service object. can I access the registry from there?
2. Inject the em to a static variable of a base class for the services in the bootstrap.
What is the correct way yo do it?
thanks
I think generally the best way to do it is to pass the entitymanager as an argument to the constructor.
This allows you to easily replace the entitymanager for example when doing unit tests, and unlike your approaches of 1 and 2, it does not depend on behavior in a base class or global data (the registry is a lot like a global variable)
What you could do to avoid touching the EM in your controllers is using a dependency injection container, such as the one in Symfony2 or the one in ZF2 (not sure if that component is very stable yet).
Another perhaps slightly simpler approach would be to have a sort of a "service locator" object, which you would use in the controller to get instances of your services. You could initialize the locator in your bootstrap with the services, or perhaps with a factory class which creates them.
In any case you will probably require at least some kind of an intermediate object in the controller. Personally I don't really see an issue with simply using the EM itself, unless you have some other reasons besides just not wanting to.
There's nothing wrong, IMO, with letting your controllers know about the EM. I typically use a Zend_Application_Resource to bootstrap Doctrine. That resource facilitates a bootstrap resource called "doctrine" which has an EM available. The abstract controller implements and em() method, which returns the EM.
When instantiating service classes, the constructor simply injects the EM via a call to $this->em() at constructor time.
This is nice, as many times, simple controller actions don't need any special service class, but can instead get away with doing $entity = $this->em()->getRepository('Some\Entity')->find(1); In those cases, I don't see any reason for additional redirection via a service class.

Categories