-
Notifications
You must be signed in to change notification settings - Fork 10.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
[11.x] Allow for contextual bindings using attributes #51115
Conversation
Thanks for submitting a PR! Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface. Pull requests that are abandoned in draft may be closed due to inactivity. |
This is great, there's currently no way to do this, even by hijacking It would be great to also add a "after" hook, so we can act on a dependency that just got resolved from the container. This would be very useful in many scenarios, not just for framework stuff, but for business logic as well: public function __construct(
#[OnTenant(Tenant::FLY7)]
private readonly Connector $connector
) {} Container::getInstance()->afterResolvingAttribute(function (OnTenant $attribute, Connector $connector) {
$connector->onTenant($attribute->tenant);
}); |
@ollieread are you still working on this one? |
I'm willing to take over the pull request if necessary. 👍 |
I intend to, but work has currently taken over. It's on a post-it on my monitor, I think I just need to write the tests, though if you can provide feedback ahead of that it would be appreciated. |
I think in its current state, the feature would not be flexible enough. Having the ability to act on an already-resolved dependency instead of having to resolve it manually would offer a few more possibilities. I wouldn't be able to implement |
What you're requesting would remove all the flexibility from this solution. Only acting on resolved dependencies removes the majority of the flexibility and provides no benefit, as this is already possible. In your example, you're hardcoding the reference to a tenant, so this solution wouldn't be applicable to you anyway, as you're looking for a dynamic solution to a static problem. If you have code that only needs to work when a particular tenant is resolved, don't call it unless that tenant is resolved. |
I'm suggesting an addition to your solution, not a replacement. 👍
Not sure what you mean here, sorry. I tried your PR locally and I couldn't find an application to my business logic use cases, unfortunately. All of them would require to modify an instance of an injected dependency, which itself would need to be resolved from the container due to their own dependencies. I'll just attempt another PR myself after, it's fine. 👍 |
In the example that you gave, the usage of an attribute is completely redundant and over-engineered. You want to call public function __construct(
private readonly Connector $connector
) {
$this->connector->onTenant(Tenant::FLY7);
}
Everything you would like to do can already be done, and you can still use the container to resolve an instance and modify it. |
I disagree with it being over-engineered, it's practical DX when this is used in many different places. This is not the subject of this PR though.
Your suggestion is what I'm doing already, and something I'd rather do through an attribute.
It's the same as hardcoding
As I said, your PR won't work for this—I tested it—unless you replace Let's just agree to disagree, go ahead with the PR 👍 |
Thanks @ollieread. Feel free to resubmit this then when you're ready. @innocenzi feel free to attempt your own PR if you want to 👍 |
Note
I will update this with tests and some default implementations before marking for review. I've created it as a draft PR in case anyone wishes to feedback.
This PR adds functionality to allow users to register PHP 8 attributes and associated handlers with the container to add in resolving dependencies. All attributes must implement the
Illuminate\Contracts\Container\ContextualAttribute
marker interface. The attributes should be added to parameters, and will trump normal resolution.The primary use of this sort of functionality would be to have dependency injection work with auth guards, connections, and other things where multiple instances exist with no difference from the perspective of the container.
Let's say for example that you have a platform that uses two guards, and different classes depend on different ones. Currently, the only solution to this is to manually get the guard, or create a contextual binding for each class. Instead, you could achieve it like so.
First, you create the attribute.
Then you register it with the container.
Once this is done, you can use the attribute with dependency injection.