Skip to content

[DI][HttpKernel] Make bind also apply to controller arguments #25960

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

Closed
weaverryan opened this issue Jan 29, 2018 · 4 comments
Closed

[DI][HttpKernel] Make bind also apply to controller arguments #25960

weaverryan opened this issue Jan 29, 2018 · 4 comments

Comments

@weaverryan
Copy link
Member

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? yes-ish
Symfony version 4.1

Hi guys!

  1. controller "action-injection" is a shortcut we added for convenience.
  2. action-injection works like constructor or method injection for services, but it does not work the same with bind: bind applies to the constructor & methods, but it does NOT work for actions.

For example:

services:
    _defaults:
        bind:
            $isDebug: '%kernel.debug%'
/**
 * @Route("/foo/bar")
 */
public function foo($isDebug)
{
}

This will fail. I'd like this to work - because it would make action-injection consistent with other injection. And I think it shouldn't be too difficult: (A) take the binding rules that apply to the controller service, (B) use those to create a map (similar to what we do for the service injection) of controller+argname => value and (C) create another argument (with low priority) that will fill in the argument.

Does anyone see any issues? I'm creating in the hope that someone will take it on :). I can... eventually... would really like to have this.

Cheers!

@linaori
Copy link
Contributor

linaori commented Jan 29, 2018

I'm a little worried that this will heavily conflict with request attributes. It will also become a lot more magic than it already is. Currently it already confuses developers that you can put services, stateful objects and request parameters in there. When adding the values from bind, it will make it even more complex, especially as now non-strings and non-objects can also be passed.

/**
 * Works:
 * @Route("/foo/bar")
 */
public function foo(bool $isDebug);

/**
 * Fails:
 * @Route("/foo/{isDebug}")
 */
public function bar(bool $isDebug);

Personally I'm against ever recommending mixing stateful and non stateful in action arguments. It's also what I recommend when people ask questions about why autowiring is complaining about an entity, or when they somehow managed to entangle a data object into the service container and it's being injected, instead of processed like a normal argument.

For the sake of consistency, I think it's good to add, but be aware of the "magic" and inconsistency in types it adds as well. I might not agree with the service arguments feature, but it should at least be feature complete.

@weaverryan
Copy link
Member Author

For the sake of consistency, I think it's good to add, but be aware of the "magic" and inconsistency in types it adds as well. I might not agree with the service arguments feature, but it should at least be feature complete.

I hear your concerns and my hope is that by making it feature-complete, it can be explained a bit better. For example:

Actions have the exact same behavior as __construct() methods PLUS a few special things like the Request, entity objects, etc.

Whereas right now, the explanation is more nuanced, since the first part of my above sentence is not true :).

We would give this new arg resolver a low priority, so wildcards would win when there is a conflict.

@linaori
Copy link
Contributor

linaori commented Jan 30, 2018

Could also log a warning when there's a conflict (and exception in dev). But this can only be detected run-time sadly. The container compilation won't know what route parameters will be available, though it can make a guess when it's not typehinted as string or object.

Now one more issue I suspect might happen. Argument Value Resolvers can be used to cast values. Let's say the type is int and it comes in as /page/{pageNumber}/. $pageNumber would be a string, so I could use an AVR to cast it to an int, based on the type. Now if this one conflicts with the bind values, it would also cause an issue, as my custom AVR might not be called properly (edge-case).

I'm curious about one thing though, does bind already work for services, or is it not working at all?

@weaverryan
Copy link
Member Author

weaverryan commented Jan 30, 2018

bind does already work for services - works very nicely actually (you can even add a bind undeto _defaults to make app-wide conventions). So adding to args makes them consistent with services :).

Update: I misunderstood the question initially. But yes, you can use bind with services, and it DOES pass that service to controller arguments. Non-services are just explicitly made to not work... mostly because the underlying way the resolver works is that it uses a service locator (so, only services could go in there). See #24600 where we made sure that scalars didn't work.

About the casting potential conflict, that shouldn’t be a problem because this new arg resolver would have a low priority, right? Your arg resolver would “win”, which is what we want, since it’s doing it’s work becaue there is a route wildcard by that name. It’s still weird if you’re expecting bind to win, but at least it’s predictable.

@fabpot fabpot closed this as completed Mar 25, 2018
fabpot added a commit that referenced this issue Mar 25, 2018
…uments (weaverryan)

This PR was merged into the 4.1-dev branch.

Discussion
----------

Adding support to bind scalar values to controller arguments

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #25960
| License       | MIT
| Doc PR        | symfony/symfony-docs#9477

Hi guys!

This fixes (I think) the last rough edge with autowiring & error messages. 100% credit to @nicolas-grekas for this implementation - he has generously allowed me to steal his code in return for writing the test. I did test this on a real project.

Cheers!

Commits
-------

2c7198c Adding support to bind scalar values to controller arguments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants