Skip to content

Conversation

@iotron
Copy link
Contributor

@iotron iotron commented Jan 2, 2026

Summary

Fixes state overwrites when multiple Group::make()->relationship() components access the same HasOne relationship across different tabs.

Problem

The saveRelationshipsBeforeChildrenUsing callback already coordinates multiple Groups - only the first Group saves, collecting data from all Groups:

// Existing save logic - already coordinated
$findComponentsWithThisRelationship($component->getRootContainer());

if (filled($componentsWithThisRelationship) && (Arr::first($componentsWithThisRelationship) !== $component)) {
    return;  // Skip if not first component
}

// First component saves data from ALL Groups
foreach ($componentsWithThisRelationship as $componentWithThisRelationship) {
    $data = [...$data, ...$componentWithThisRelationship->getChildSchema()->getState(...)];
}

However, loadStateFromRelationshipsUsing lacks this coordination - it calls fillFromRelationship() for every Group, causing state overwrites:

// Current load logic - NOT coordinated
$this->loadStateFromRelationshipsUsing(static function ($component): void {
    $component->clearCachedExistingRecord();
    $component->fillFromRelationship();  // Called for EVERY Group - overwrites previous state
});

With 3+ Groups:

  1. Group 2 fills → hydrates (normalizes FileUpload to UUID-keyed array)
  2. Group 3 fills → overwrites Group 2's normalized state
  3. Group 4 fills → overwrites Group 3's normalized state
  4. FileUpload has raw string instead of UUID-keyed array → type errors

Errors:

  • foreach() argument must be of type array|object, string given at BaseFileUpload.php:742
  • Argument #2 ($rawState) must be of type ?array, string given at RichEditor.php:346

Solution

Apply the same coordination pattern from saveRelationshipsBeforeChildrenUsing to loadStateFromRelationshipsUsing:

  • First component: calls fillFromRelationship() (sets raw state + hydrates all)
  • Subsequent components: only call hydrateState() (state already set, applies StateCasts)

Benefits

  • Mirrors existing pattern - same coordination logic already used in save operation
  • No method signature changes - fillFromRelationship() unchanged
  • No new helper methods - uses existing hydrateState()
  • Minimal code change - ~35 lines added

Testing

Reproduction repo: https://github.com/iotron/filament-bug-reproduction

Verified both errors are fixed after applying this change.

Fixes #18826
Related to #18718, #18727

…onents

When multiple Group::make()->relationship() components access the same
HasOne relationship across different tabs, subsequent Groups were
overwriting state normalization (StateCasts) performed by earlier Groups.

This caused type errors in components like FileUpload and RichEditor:
- foreach() error in BaseFileUpload.php:742
- $rawState type error in RichEditor.php:346

The fix coordinates multiple Groups in loadStateFromRelationshipsUsing,
mirroring the existing saveRelationshipsBeforeChildrenUsing pattern:
- First component: calls fillFromRelationship() (sets + hydrates state)
- Subsequent components: only call hydrateState() (applies StateCasts)

Fixes filamentphp#18826
Related to filamentphp#18718, filamentphp#18727
@iotron iotron force-pushed the fix/group-relationship-state-overwrite branch from 1173b20 to 844656d Compare January 2, 2026 10:42
@danharrin danharrin added the bug Something isn't working label Jan 2, 2026
@danharrin danharrin added this to the v4 milestone Jan 2, 2026
@github-project-automation github-project-automation bot moved this from Todo to In Progress in Roadmap Jan 2, 2026
@danharrin danharrin merged commit 6c3d44d into filamentphp:4.x Jan 2, 2026
24 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Roadmap Jan 2, 2026
@Odion-DS
Copy link
Contributor

@danharrin That's a bit silly because there are actually cases where that is desirable, especially when the components have different state paths.

@danharrin
Copy link
Member

What's silly?

@Odion-DS
Copy link
Contributor

Sorry, I used a slightly wrong translation for that word. What I meant is that this is sometimes desirable, because when components have different state paths, the behavior should remain the same as before.

@danharrin
Copy link
Member

Okay, that just sounds like a bug - we should compare the state paths of the components to ensure they are the same before using the behaviour. Do you want to PR that?

@Odion-DS
Copy link
Contributor

My boss says I should do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

State overwrites when multiple Group->relationship() access same HasOne relationship

3 participants