In Filament, you can attach/detach a relationship with the Relation Manager. But what if you want to refresh the main form after creating/editing/deleting a record in the Relation Manager? Let me show you how to do it.
In this example, we will have a Product
Model with many Payment
. After adding a new payment, the form will be refreshed to show the sum
of payments.
Laravel Project Structure
First, let's see what the relations in the Model and Filament Resource look like.
app/Models/Product.php:
use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Product extends Model{ protected $fillable = [ 'name', ]; public function payments(): BelongsToMany { return $this->belongsToMany(Payment::class); } public function totalPayments(): int { return $this->payments->sum('total'); }}
The Payment
Model only has two fields.
app/Models/Payment.php:
class Payment extends Model{ protected $fillable = [ 'plan_type', 'total', ];}
Filament Resource Form
For the Filament Resource, the form for the product looks like this.
app/Filament/ProductResource.php:
use App\Models\Product;use Filament\Forms;use Filament\Forms\Form; class ProductResource extends Resource{ protected static ?string $model = Product::class; protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; public static function form(Form $form): Form { return $form ->schema([ Forms\Components\Group::make() ->schema([ Forms\Components\Section::make() ->schema([ Forms\Components\TextInput::make('name'), ]), ]) ->columnSpan(['lg' => fn (?Product $record): int => $record ? 2 : 3]), Forms\Components\Group::make() ->schema([ Forms\Components\Section::make() ->schema([ Forms\Components\Placeholder::make('created_at') ->label('Created at') ->content(fn (Product $record): ?string => $record->created_at?->diffForHumans()), Forms\Components\Placeholder::make('payments_count') ->label('Total payments') ->content(fn (Product $record) => $record->totalPayments()), ]), ]) ->columnSpan(['lg' => 1]) ->hiddenOn('create') ])->columns(3); } // ...}
This is what the edit page looks like. We will update the Total payments
value.
Filament Relation Manager
And lastly, we need a Relation Manager.
app/Filament/ProductResource/RelationManagers/PaymentsRelationManager.php:
class PaymentsRelationManager extends RelationManager{ protected static string $relationship = 'payments'; public function form(Form $form): Form { return $form ->schema([ Forms\Components\Select::make('plan_type') ->options([ 'monthly' => 'Monthly', 'yearly' => 'Yearly', ]) ->required(), Forms\Components\TextInput::make('total') ->required() ->numeric(), ]); } public function table(Table $table): Table { return $table ->recordTitleAttribute('plan_type') ->columns([ Tables\Columns\TextColumn::make('plan_type'), Tables\Columns\TextColumn::make('total'), ]) ->filters([ // ]) ->headerActions([ Tables\Actions\CreateAction::make(), ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]) ->emptyStateActions([ Tables\Actions\CreateAction::make(), ]); }}
We have two simple inputs for the create form in the relation manager.
Finally, the preparation phase is done.
Refreshing Update Form
This is where we get to the actual topic of this tutorial.
We need to refresh the edit form so that the total payments will get updated. To do this, we need to dispatch an event after the action in the relation manager and listen for it in the edit page.
app/Filament/ProductResource/RelationManagers/PaymentsRelationManager.php:
use Livewire\Component; class PaymentsRelationManager extends RelationManager{ // ... public function table(Table $table): Table { return $table ->recordTitleAttribute('plan_type') ->columns([ Tables\Columns\TextColumn::make('plan_type'), Tables\Columns\TextColumn::make('total'), ]) ->filters([ // ]) ->headerActions([ Tables\Actions\CreateAction::make() ->after(function (Component $livewire) { $livewire->dispatch('refreshProducts'); }), ]) ->actions([ Tables\Actions\EditAction::make() ->after(function (Component $livewire) { $livewire->dispatch('refreshProducts'); }), Tables\Actions\DeleteAction::make() ->after(function (Component $livewire) { $livewire->dispatch('refreshProducts'); }), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make() ->after(function (Component $livewire) { $livewire->dispatch('refreshProducts'); }), ]), ]) ->emptyStateActions([ Tables\Actions\CreateAction::make() ->after(function (Component $livewire) { $livewire->dispatch('refreshProducts'); }), ]); }}
Here, we are using after
lifecycle hook to fire a Livewire event after the action.
Next, we need to listen for the event and refresh the page.
app/Filament/ProductResource/Pages/EditProduct.php:
use Livewire\Attributes\On; class EditProduct extends EditRecord{ // ... #[On('refreshProducts')] public function refresh(): void { }}
After creating a new payment, editing, or deleting the Total payments
will be updated without page refresh.
If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.
Thank you very much for this content. I was looking for this.
this is awesome 👍
not working on filament v2, anyone can help
livewire v2 has completely different syntax for events. check the docs for livewire v2 and adjust to your needs
thanks nerijus, just got this trick on youtube. it works like charms!
on parent edit page protected $listeners = ['refresh' => 'render'];
on relation managers page > create /edit /delete action $livewire->emit('refresh');