-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to inject objects to actions without the requirement to define them in the container #18388
Comments
Arguments 1 and 2 are invalid.
You are comparing a container misused as a service locator with dependency injection; these are not the same things.
The argument that people can write bad code in other ways is not an argument to add new ways for people to create bad code. If argument 3 is factually correct than I would counter-propose to remove that functionality from Yii3. There is no reason to support this bad behavior out of the box, if people want to use their own implementation they are free to do so, but our core implementation should promote proper use of DI. |
Ok, let me add some more code to my example. Let's say I have my service class and it also has a dependency - a repository object. class MyService
{
private MyRepository $repository;
public function __construct(MyRepository $repository)
{
$this->repository = $repository;
}
} If I skip the definition of MyRepository inside the container, it will be created for me by the container (with its dependencies if there are any). That's why I'm writing that the behavior differs from other parts of the framework. So when I'm injecting to the action I'm actually expecting the same behavior. If this behavior is undesirable we should force to define any class that is supposed to be injected (and their dependencies) inside the container. It is also interesting how other frameworks implement action injection. May be it is not the main argument but it is also something to be considered. As far as I remember Symfony allows to inject objects from App namespace (with excludes set in the config). |
But proper code wouldn't do that... Instead you would have an interface that is implemented by your service, then your action requires the interface not a concrete implementation. This means you must configure that relation in your container since it won't be able to construct an interface. In your example if you define |
@SamMousa in theory you're correct. In practice, you're not always creating an interface for every possible service especially if you're more than sure it won't be replaced ever. |
True, but even then, configuration in the DI container is just one line. At the very least if we do it, it should become optional imo. |
I think either we don't do it or we do it by default. Making it optional doesn't make sense to me since you'll have to take that into account when developing extensions. |
In that case my vote is against it.. It doesn't do any favors to code quality and we shouldn't forget that this is mostly about injecting classes that have no configuration and therefore can just as well be constructed.. In most cases we will either inject a service defined in the container or a component defined in the application, both cases are supported. |
Makes sense. Won't be implemented. |
If I understood you correctly you mean that we inject mostly services and they can be constructed with a contstructor call (new Service()). But services has a lot of dependencies, mostly components that can be injected into it instead of using service locator (for example a Mailer). So we can not just instantiate a service, we need to inject it. And this dependencies can be constructed automatically for us, so we can completely skip a service definition from the container. Now I can use Yii::$container->get() to get any service from a container.
You are right, it is not the proper way to do. But I can also inject dependencies via controller's constructor (overriding it). It is the proper way of using DI, isn't it? And I can do it without defining the service in the container. My main point is an illogical behavior that allows constructing dependencies in one place but denies it in another one. Why is an action injection a special case that we should always take care of? So my main argument that we have an illogical behavior that contradicts the rest parts of DI usage in the framework. |
You misunderstand. Services you want to inject. But services most likely have some configuration and therefore will already be defined in your components or in DI config itself (and thus you can already inject them) |
Ok, I got it. But they can have no explicit configuration (when all their dependencies can be resolved automatically by the container). In this case their definition in the container is redundant in my opinion. And in my personal workflow they do not have explicit configuration in most cases. And what about my other arguments about an incosistent behavior and Yii3? |
I don't think the Yii3 argument is relevant. The whole point of a major
upgrade is to allow for changing behavior.
Also, I'm not too deep into Yii3, but I'd probably argue against action
methods as a whole. Action classes make more sense and solve the discussion
by using pure constructor injection.
…On Mon, 23 Nov 2020, 08:08 dmotitsk, ***@***.***> wrote:
But services most likely have some configuration and therefore will
already be defined in your components or in DI config itself (and thus you
can already inject them)
Ok, I got it.
But they can have no explicit configuration (when all their dependencies
can be resolved automatically by the container). In this case their
definition in the container is redundant in my opinion. And in my personal
workflow they do not have explicit configuration in most cases.
And what about my other arguments about an incosistent behavior and Yii3?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#18388 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEFRTKJZSU3DAXTBAACEZ3SRIC75ANCNFSM4TXGYYJA>
.
|
I constatly refer to Yii3 because as I understand this post https://github.com/yiisoft/yii2/issues/17722#issue-535273746 the whole idea was to make it easier to migrate from Yii2 to Yii3. And it is not the situation when we have a feature for a long time in Yii2 and we rework and reintroduce it in the next major version. As for me if this was decided to be implemented after the feature freeze it should be done keeping Yii3 in mind.
I used action classes with constructor injection before action injection became available. It is OK but for the simple cases it can be an overkill. Either way if Yii3 completely disallow action methods of controller it will be too different from other frameworks. As for me action injection is a great "framework-level sugar". You implemented a great feature, but as for me, you were too strict requiring service definition in the container. First time when I wrote an action after this feature was presented I was very surprised that my service wasn't injected and I got an exception. Because I was used to the fact that the container creates undefined classes automatically. And this is what I call "illogical behavior". I expected one thing from my experience with the framework, but got the opposite. These feature is cool and it makes much convinient to define actions but I think we can move it one step further as it is done in Yii3 and other frameworks. Those who want to explicitly define services in the container can continue to do this. Those who want to ommit services with empty $params argument will be able to skip this. And it will not take much work to implement. |
We didn't have this for a long time, if you want an easy migration path you don't use the Yii2 version and wait for Yii3.
I don't see an issue here, you get surprised but it fails hard so you learn and you can now write cleaner code because of it.
And I think that would hurt code quality instead of helping it. Regardless, I'm done discussing this, I think we've both have repeated our arguments and further discussion serves no purpose. Don't get me wrong, I don't mind the input or criticism, but I'm not going to spend time in a debate when there are no new arguments coming forward :) |
Hi @dmotitsk, i think i have a solution for you. I am missing it as well, it would be really nice to have proper RequestModel auto injected in action method, without setting it up manually. so my solution is (still in work in progress, but it is already working): i created an action filter, which makes possible to auto register missing class names just before the action run, and clear it after run. It uses the same logic as bindInjectedParams
In controller following markup can be used:
Request object itself:
for this i did registered main request in DI container (only one request should exists)
|
This PR #18032 added action injection. But currently there is a requirement to define objects that are to be injected in the container. If the container knows nothing about the class type the exception will be thrown.
So the following code will work without definition of the type MyService in the container:
and this code won't work without defining MyService in the container (it will throw an Exception):
The author of this PR @SamMousa has arguments against allowing injecting arbitary objects. You can read them here https://github.com/yiisoft/yii2/pull/18032#issuecomment-727220098.
But I have several arguments in favor of this idea.
The behavior of the container differs from other parts of the framework. In any other part if there is no definition in the container about the type, it will just try to create the object of this type. In action injection it will throw an exception.
As we can see in the example above developers can create any objects inside the action through $container->get() and disallowing them direct injection via function arguments doesn't protect against misunderstanding of what should be injected and what should not be.
As far as I understand this feature (action injection) was postponed for a long time. And the main argument to implement it in Yii2 framework was that it would be allowed in the 3rd version. So it will be easy in future to migrate from Yii2 to Yii3.
As I can see currently Yii3 allows to inject objects to the actions without the requirement to declare their types in the container (correct me if I'm wrong). So it would be more logical to allow the same thing in Yii2.
It can be fixed very easy. Just need to rollback this commit https://github.com/yiisoft/yii2/pull/18032/commits/03b7c845ca25745785bb4ff5ef5d2cbc760bcc74.
The text was updated successfully, but these errors were encountered: