[9.x] Use model route key when route parameter does not specifiy custom binding field but a different parameter does - #42425
Merged
Conversation
Member
|
Thanks! |
Member
|
FYI: #43255 |
Contributor
Author
|
Looks good. Sorry about all the issues this PR has caused 😞 |
Member
|
@ksassnowski don't worry about it! :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note: I'm unsure to which branch I should send this bugfix since the implementation of
RouteUrihas changed between8.xand9.x.9.xusesstr_containsinstead ofstrpos. I might have to send two separate PRs to each respective branch? Help? 😅Summary
This PR fixes a special circumstance where the wrong model route key would get used when substituting route parameters. For a tl;dr of what the bug is, check out the test I added to
RoutingUrlGeneratorTest.phpthat reproduces the error. For the longer explanation, keep reading.Reproducing the bug
Let's say I have two models,
UserandPost. TheUsermodel overrides thegetRouteKeyNamemethod so the user'susernamefield gets used during route generation instead of theid. ThePostmodel is kept vanilla.Now, let's define a route like this.
We specify an explicit binding field for the
postso it gets scoped on the user. For the user, we don't specify an explicit binding key, so we expect it to use theusernamelike we've configured. This works as expected if we generate the route url and pass the parameters as an associative array.However, if we pass the parameters without explicit keys it breaks.
If we explicitly define the binding field for the
userparameter as well, it works again as expected.I have added a test to the
RoutingUrlGeneratorTestfile that reproduces this error.Why the bug happens
When the
RouteUriclass parses the URI, it extracts all binding fields from the URI. This array then gets saved on theRouteobject's$bindingFieldsproperty. So our URI ofgets turned into
So far so good, but note that the
userparameter is missing since it doesn't define a an explicit binding field (no spoilers, but this is important).When the
UrlGeneratortries to resolve the route parameters, it loops of the parameters and calls thebindingFieldFormethod of theRouteobject for each of them (https://github.com/laravel/framework/blob/9.x/src/Illuminate/Routing/UrlGenerator.php#L483).This method is passed the key of the current parameter we're looping over. It then checks if that key is an integer, and if so, extracts the binding field from the route's
bindingFieldsarray by index.Going back to our route above, the
bindingFieldsarray currently looks like this.But since it's trying to look up the binding field by index, it ends doing this instead.
Since
useris the first parameter we passed to theroutecall, it tries to look up the binding field with index0in this array. Which is why theidfield gets mistakenly used for theuserparameter.Also, at this point "binding fields" has stopped sounding like a word.
Changes
To fix this bug, I changed the
parsemethod of theRouteUriclass to keep track of all parameters, instead of only parameters with an explicit binding field. So when we first parse our URI, we get back this instead:Before returning, I check if this array contains any values that are not
null, meaning we have at least one explicit binDinG fIeLd. In that case, we keep the whole array—includingnullvalues—so the index positions line up as expected when callingarray_valueson it. In any other case, we simply return an empty array (like before).Other changes
I've refactored the tests for the
RouteUriclass to use a data provider instead of having all assertions in a single test. That made figuring out which test case actually broke quite a bit easier.