diff --git a/packages/actions/src/Concerns/InteractsWithActions.php b/packages/actions/src/Concerns/InteractsWithActions.php index d09ac6590d..6db317633d 100644 --- a/packages/actions/src/Concerns/InteractsWithActions.php +++ b/packages/actions/src/Concerns/InteractsWithActions.php @@ -332,6 +332,11 @@ trait InteractsWithActions return $result; } + public function forceRender(): void + { + app(PartialsComponentHook::class)->forceRender($this); + } + protected function partiallyRenderActionParentSchema(Action $action): void { $actionSchema = $action->getSchemaContainer() ?? $action->getSchemaComponent()?->getContainer(); diff --git a/packages/actions/src/CreateAction.php b/packages/actions/src/CreateAction.php index 88033f20ef..d681f0d988 100644 --- a/packages/actions/src/CreateAction.php +++ b/packages/actions/src/CreateAction.php @@ -23,6 +23,8 @@ class CreateAction extends Action protected bool | Closure $canCreateAnother = true; + protected bool | Closure $shouldForceRenderAfterCreateAnother = false; + protected ?Closure $modifyCreateAnotherActionUsing = null; protected ?Closure $preserveFormDataWhenCreatingAnotherUsing = null; @@ -107,6 +109,14 @@ class CreateAction extends Action $schema->model($record)->saveRelationships(); if ($arguments['another'] ?? false) { + if ($this->shouldForceRenderAfterCreateAnother()) { + $livewire = $this->getLivewire(); + + if (method_exists($livewire, 'forceRender')) { + $livewire->forceRender(); + } + } + $this->callAfter(); $this->sendSuccessNotification(); @@ -157,6 +167,13 @@ class CreateAction extends Action return $this; } + public function forceRenderAfterCreateAnother(bool | Closure $condition = true): static + { + $this->shouldForceRenderAfterCreateAnother = $condition; + + return $this; + } + /** * @deprecated Use `createAnother()` instead. */ @@ -192,6 +209,11 @@ class CreateAction extends Action return true; } + public function shouldForceRenderAfterCreateAnother(): bool + { + return (bool) $this->evaluate($this->shouldForceRenderAfterCreateAnother); + } + public function getRelationship(): Relation | Builder | null { return $this->evaluate($this->getRelationshipUsing) ?? $this->getTable()?->getRelationship() ?? $this->getHasActionsLivewire()?->getDefaultActionRelationship($this); diff --git a/packages/support/src/Livewire/Partials/PartialsComponentHook.php b/packages/support/src/Livewire/Partials/PartialsComponentHook.php index eced38fb9a..e92a56b179 100644 --- a/packages/support/src/Livewire/Partials/PartialsComponentHook.php +++ b/packages/support/src/Livewire/Partials/PartialsComponentHook.php @@ -17,6 +17,10 @@ class PartialsComponentHook extends ComponentHook { public function shouldSkipRender(): bool { + if ($this->shouldForceRender()) { + return false; + } + if (! $this->isLackingPartialRendersToCoverAllCallsAndUpdates()) { return true; } @@ -62,6 +66,11 @@ class PartialsComponentHook extends ComponentHook return ($updatesCount + $callsCount) !== intval($this->storeGet('partialRendersCount') ?? 0); } + public function shouldForceRender(): bool + { + return store($this->component)->get('forceRender', false); + } + public function shouldRenderMountedActionOnly(): bool { if (! property_exists($this->component, 'mountedActions')) { @@ -100,6 +109,10 @@ class PartialsComponentHook extends ComponentHook public function dehydrate(ComponentContext $context): void { + if ($this->shouldForceRender()) { + return; + } + $partials = []; $renderAndQueuePartials = function (Closure $getPartialsUsing) use (&$partials): void { @@ -173,6 +186,11 @@ class PartialsComponentHook extends ComponentHook $this->recordPartialRender($component); } + public function forceRender(Component $component, bool $forceRender = true): void + { + store($component)->set('forceRender', $forceRender); + } + public function renderPartial(Component $component, Closure $renderUsing): void { store($component)->push('partials', $renderUsing); diff --git a/tests/src/Fixtures/Models/Department.php b/tests/src/Fixtures/Models/Department.php index 77f9a7b8d8..9a0269ec56 100644 --- a/tests/src/Fixtures/Models/Department.php +++ b/tests/src/Fixtures/Models/Department.php @@ -13,6 +13,8 @@ class Department extends Model use HasFactory; use SoftDeletes; + protected $guarded = []; + protected static function newFactory() { return DepartmentFactory::new(); diff --git a/tests/src/Fixtures/Resources/Departments/Schemas/DepartmentForm.php b/tests/src/Fixtures/Resources/Departments/Schemas/DepartmentForm.php index 1f1ca8feac..504c146512 100644 --- a/tests/src/Fixtures/Resources/Departments/Schemas/DepartmentForm.php +++ b/tests/src/Fixtures/Resources/Departments/Schemas/DepartmentForm.php @@ -2,6 +2,7 @@ namespace Filament\Tests\Fixtures\Resources\Departments\Schemas; +use Filament\Forms\Components\TextInput; use Filament\Schemas\Schema; class DepartmentForm @@ -10,7 +11,7 @@ class DepartmentForm { return $schema ->components([ - // + TextInput::make('name')->required(), ]); } } diff --git a/tests/src/Fixtures/Resources/Tickets/RelationManagers/DepartmentsRelationManager.php b/tests/src/Fixtures/Resources/Tickets/RelationManagers/DepartmentsRelationManager.php index c544ec1764..80f7ec7bc6 100644 --- a/tests/src/Fixtures/Resources/Tickets/RelationManagers/DepartmentsRelationManager.php +++ b/tests/src/Fixtures/Resources/Tickets/RelationManagers/DepartmentsRelationManager.php @@ -4,7 +4,9 @@ namespace Filament\Tests\Fixtures\Resources\Tickets\RelationManagers; use Filament\Actions\CreateAction; use Filament\Resources\RelationManagers\RelationManager; +use Filament\Schemas\Schema; use Filament\Tables\Table; +use Filament\Tests\Fixtures\Resources\Departments\Schemas\DepartmentForm; use Filament\Tests\Fixtures\Resources\Departments\Tables\DepartmentsTable; class DepartmentsRelationManager extends RelationManager @@ -18,4 +20,9 @@ class DepartmentsRelationManager extends RelationManager CreateAction::make(), ]); } + + public function form(Schema $schema): Schema + { + return DepartmentForm::configure($schema); + } } diff --git a/tests/src/Panels/Resources/RelationManagerTest.php b/tests/src/Panels/Resources/RelationManagerTest.php index f76e062461..2e68cbd299 100644 --- a/tests/src/Panels/Resources/RelationManagerTest.php +++ b/tests/src/Panels/Resources/RelationManagerTest.php @@ -11,6 +11,7 @@ use Filament\Actions\RestoreAction; use Filament\Actions\RestoreBulkAction; use Filament\Actions\Testing\TestAction; use Filament\Actions\ViewAction; +use Filament\Resources\RelationManagers\RelationManager; use Filament\Tests\Fixtures\Models\Department; use Filament\Tests\Fixtures\Models\Ticket; use Filament\Tests\Fixtures\Policies\DepartmentPolicy; @@ -18,8 +19,10 @@ use Filament\Tests\Fixtures\Resources\Tickets\Pages\EditTicket; use Filament\Tests\Fixtures\Resources\Tickets\RelationManagers\DepartmentsRelationManager; use Filament\Tests\Panels\Resources\TestCase; use Illuminate\Auth\Access\Response; +use Illuminate\Support\Str; use function Filament\Tests\livewire; +use function Pest\Laravel\assertDatabaseHas; uses(TestCase::class); @@ -150,3 +153,29 @@ it('renders actions based on policy', function (string $action, string $policyMe 'restore bulk action with policy returning allowed response' => fn (): array => [RestoreBulkAction::class, 'restoreAny', Response::allow(), true, true, true], 'restore bulk action with policy returning false' => fn (): array => [RestoreBulkAction::class, 'restoreAny', false, false, true, true], 'restore bulk action with policy returning denied response' => fn (): array => [RestoreBulkAction::class, 'restoreAny', Response::deny(), false, true, true]]); + +it('can force render relation manager after create another', function (): void { + $ticket = Ticket::factory() + ->create(); + + CreateAction::configureUsing(function (CreateAction $action): void { + $action->forceRenderAfterCreateAnother(fn (mixed $livewire): bool => $livewire instanceof RelationManager); + }); + + $action = TestAction::make(CreateAction::class)->table(); + + livewire(DepartmentsRelationManager::class, ['ownerRecord' => $ticket, 'pageClass' => EditTicket::class]) + ->assertSuccessful() + ->assertCountTableRecords(0) + ->assertActionExists($action) + ->mountAction($action, ['another' => true]) + ->fillForm([ + 'name' => $name = Str::random(), + ]) + ->callMountedAction() + ->assertHasNoFormErrors() + ->assertCountTableRecords(1) + ->assertSeeText($name); + + assertDatabaseHas(Department::class, ['name' => $name]); +});