mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-01 15:09:33 +08:00
Fix: forget a closed action's cached schema when it is unmounted (#20361)
* uncache mounted schemas * Refine mounted action schema cache cleanup Amp-Thread-ID: https://ampcode.com/threads/T-01a04e2a-fe3c-731e-b615-9a4456fdceaa --------- Co-authored-by: Dan Harrin <git@danharrin.com>
This commit is contained in:
@@ -421,13 +421,22 @@ trait InteractsWithActions /** @phpstan-ignore trait.unused */
|
||||
$this->mountedActions = [];
|
||||
$this->cachedMountedActions = null;
|
||||
|
||||
foreach ($this->cachedSchemas as $schemaName => $schema) {
|
||||
if (str($schemaName)->startsWith('mountedActionSchema')) {
|
||||
$this->forgetCachedMountedActionSchemas();
|
||||
|
||||
$this->mountAction($name, $arguments, $context);
|
||||
}
|
||||
|
||||
protected function forgetCachedMountedActionSchemas(int $fromNestingIndex = 0): void
|
||||
{
|
||||
foreach (array_keys($this->cachedSchemas) as $schemaName) {
|
||||
if (! str_starts_with($schemaName, 'mountedActionSchema')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (((int) substr($schemaName, strlen('mountedActionSchema'))) >= $fromNestingIndex) {
|
||||
unset($this->cachedSchemas[$schemaName]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->mountAction($name, $arguments, $context);
|
||||
}
|
||||
|
||||
public function cacheAction(Action $action): Action
|
||||
@@ -803,6 +812,12 @@ trait InteractsWithActions /** @phpstan-ignore trait.unused */
|
||||
array_pop($this->cachedMountedActions);
|
||||
}
|
||||
|
||||
// The schemas of the actions that have just closed, which are cached by nesting index: an
|
||||
// action mounted at one of those indexes later in this request would otherwise be handed
|
||||
// the schema of the action that used to be there, since `getMountedActionSchema()` reads
|
||||
// the cache before it builds anything.
|
||||
$this->forgetCachedMountedActionSchemas(fromNestingIndex: count($this->mountedActions));
|
||||
|
||||
if (! count($this->mountedActions)) {
|
||||
$action?->clearRecordAfter();
|
||||
|
||||
|
||||
@@ -483,6 +483,48 @@ describe('nested actions', function (): void {
|
||||
});
|
||||
});
|
||||
|
||||
describe('unmounting actions', function (): void {
|
||||
it('forgets a nested action schema before another action is mounted at the same index', function (): void {
|
||||
$livewire = livewire(Actions::class)
|
||||
->mountAction('staleSchemaParent')
|
||||
->mountAction('first');
|
||||
|
||||
expect(array_keys($livewire->instance()->getSchema('mountedActionSchema1')->getFlatFields()))
|
||||
->toBe(['alpha']);
|
||||
|
||||
$livewire->call('unmountThenMountAction', 'second');
|
||||
|
||||
expect(array_keys($livewire->instance()->getSchema('mountedActionSchema1')->getFlatFields()))
|
||||
->toBe(['beta']);
|
||||
});
|
||||
|
||||
it('forgets a root action schema before another action is mounted at the same index', function (): void {
|
||||
$livewire = livewire(Actions::class)
|
||||
->mountAction('staleSchemaParent');
|
||||
|
||||
expect(array_keys($livewire->instance()->getSchema('mountedActionSchema0')->getFlatFields()))
|
||||
->toBe(['parentField']);
|
||||
|
||||
$livewire->call('unmountThenMountAction', 'staleSchemaOther');
|
||||
|
||||
expect(array_keys($livewire->instance()->getSchema('mountedActionSchema0')->getFlatFields()))
|
||||
->toBe(['otherField']);
|
||||
});
|
||||
|
||||
it('preserves the schema of an action that remains mounted', function (): void {
|
||||
$livewire = livewire(Actions::class)
|
||||
->mountAction('staleSchemaParent')
|
||||
->mountAction('first');
|
||||
|
||||
$livewire->call('unmountAction');
|
||||
|
||||
expect(array_keys($livewire->instance()->getSchema('mountedActionSchema0')->getFlatFields()))
|
||||
->toBe(['parentField'])
|
||||
->and($livewire->instance()->getSchema('mountedActionSchema1'))
|
||||
->toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('extra modal footer actions', function (): void {
|
||||
it('can mount an action that has a group in `extraModalFooterActions()`', function (): void {
|
||||
livewire(Actions::class)
|
||||
|
||||
@@ -121,6 +121,28 @@ class Actions extends Page
|
||||
])
|
||||
->action(fn () => null),
|
||||
]),
|
||||
Action::make('staleSchemaParent')
|
||||
->schema([
|
||||
TextInput::make('parentField'),
|
||||
])
|
||||
->registerModalActions([
|
||||
Action::make('first')
|
||||
->schema([
|
||||
TextInput::make('alpha'),
|
||||
])
|
||||
->action(fn () => null),
|
||||
Action::make('second')
|
||||
->schema([
|
||||
TextInput::make('beta'),
|
||||
])
|
||||
->action(fn () => null),
|
||||
])
|
||||
->action(fn () => null),
|
||||
Action::make('staleSchemaOther')
|
||||
->schema([
|
||||
TextInput::make('otherField'),
|
||||
])
|
||||
->action(fn () => null),
|
||||
Action::make('grandparentWithModalCloseCancellation')
|
||||
->schema([
|
||||
TextInput::make('grandparentValue')
|
||||
@@ -281,4 +303,10 @@ class Actions extends Page
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public function unmountThenMountAction(string $name): void
|
||||
{
|
||||
$this->unmountAction();
|
||||
$this->mountAction($name);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user