[4.x] Preserve named error bags during component rendering - #10314
Conversation
|
@hamdyelbatal122 duplicate PR ? #10307. Although, the approach taken seems different |
joshhanley
left a comment
There was a problem hiding this comment.
@hamdyelbatal122 thanks for the PR! Yeah this is a duplicate of PR #10307, but I think this implementation is better as it doesn't depend on the session. I've copied the test across from that PR as it's more inline with what was reported in the issue.
|
Hey @joshhanley, I noticed that the Unit tests for Laravel 13 were failing due to the default validation message format (which doesn't include the trailing period in newer Laravel versions). I've updated the test assertion to check for The branch has been updated and is ready for the CI runs. Thanks again for the review! |
|
@hamdyelbatal122 nah they were failing due to an upstream issue in Symfony and Laravel. It got fixed in last weeks release of Laravel. |
|
Thanks for the clarification @joshhanley, and for the review. Glad this approach works. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a Livewire rendering regression where previously shared named Laravel validation error bags (e.g. from redirects using withErrors(..., 'bag_name')) were being discarded when Livewire re-shared the global $errors variable during component rendering.
Changes:
- Preserve any previously shared
ViewErrorBagbags when Livewire shares$errors, while still overriding thedefaultbag with the component’s own error bag. - Apply the same preservation behavior to both
renderandrenderIslandhooks. - Add a unit test that reproduces a non-Livewire POST redirecting back with a named bag and verifies it remains accessible in the rendered component.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Features/SupportValidation/SupportValidation.php |
Preserves existing named error bags when sharing $errors into views during component render/island render. |
src/Features/SupportValidation/UnitTest.php |
Adds regression coverage ensuring named session error bags survive the Livewire rendering cycle. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } | ||
|
|
||
| return $errors->put('default', $this->component->getErrorBag()); |
There was a problem hiding this comment.
Two thoughts:
- Why not just use the shared error bag in the first place? aren't we effectively creating like a clone of it?
- Accessing
app('view')->getShared()has a runtime cost. Especially on a page with hundreds of livewire components, that's hundreds of calls to that. (could be memoized - but would have to be cleared for octane-like environments)
There was a problem hiding this comment.
Hi @calebporzio ,
Regarding your thoughts:
-
Using the shared error bag directly vs cloning:
SinceViewErrorBagis an object, mutating it directly (e.g. by putting the component's own default bag viaput('default', ...)) would mutate the globally shared instance. This would cause child or sibling components to overwrite the default error bag for their parent and sibling components. By cloning the sharedViewErrorBag(which creates a shallow copy of the bags array), we can safely override the'default'bag for the current component's render scope without side-effects on other components, and then safely revert back to the previous instance. -
Runtime cost of
app('view')->getShared()& Octane compatibility:
That's a great point. I have updated the code to memoize the view factory in a static property (static::$view) to avoid callingapp('view')repeatedly, and hooked into Livewire'sflush-stateevent to reset it, making it completely safe for persistent server environments like Octane.
I have also simplified the code to use clone directly instead of the manual foreach loop. I've pushed these changes to the branch!
…es in query string browser assertions
|
thanks! |
Description
This PR resolves an issue where named error bags passed from session redirects (via
.withErrors($validator, 'bag_name')) were lost during Livewire rendering.Fixes #10306
Root Cause
In
SupportValidation.php, Livewire's component validation hooks (renderandrenderIsland) overwrite the globally sharederrorsview variable with a newViewErrorBagcontaining only the component's default error bag:This completely discards any previously shared
errors(including named error bags) during the rendering of the component, making them inaccessible in Blade via@error('field', 'bag_name')or$errors->getBag('bag_name').Solution
Instead of overwriting the shared variable with a new empty bag, we retrieve the previously shared
errorsvariable first. If it is aViewErrorBag, we copy all existing bags over to the newViewErrorBagbefore putting the component's default error bag.I've also added a unit test to verify this behavior.