Free·
Update a state on page - after editing relationship
After edit a relationship, you might want to update a state on the page.

After editing a relationship, you might want to update a state on the page.
The Problem
In this scenario, we want to display a warning if a Supplier has Purchase Orders with Pending status.
The result of that is influenced when we update the child related records, so we need to check that after editing.
The Solution
Thankfully, Filament v4 has a listener to update the page, so we can resort to just ask the parent page to update, by adding a callback function on the after hook of the Edit action.
In the parent schema, we add an inline HTML Section, that will be visible only if the condition is met.
app/Filament/Resources/Suppliers/RelationManagers/PurchaseOrdersRelationManager.php
<?php
namespace App\Filament\Resources\Suppliers\RelationManagers;
use Filament\Schemas\Schema;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Textarea;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\Summarizers\Sum;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\Filter;
use Filament\Actions\EditAction;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\BulkAction;
use App\Enums\PurchaseOrderStatus;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class PurchaseOrdersRelationManager extends RelationManager
{
protected static string $relationship = 'purchaseOrders';
protected static ?string $recordTitleAttribute = 'order_number';
public function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('order_number')
->disabled()
->label('Order Number'),
Select::make('status')
->options(PurchaseOrderStatus::class)
->required(),
DatePicker::make('order_date')
->required()
->default(now()),
DatePicker::make('expected_delivery_date')
->label('Expected Delivery'),
DatePicker::make('actual_delivery_date')
->label('Actual Delivery'),
TextInput::make('total_amount')
->disabled()
->label('Total Amount')
->formatStateUsing(fn ($state) => $state ? '$' . number_format($state / 100, 2) : '$0.00'),
Textarea::make('notes')
->rows(3)
->columnSpanFull(),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('order_number')
->columns([
TextColumn::make('order_number')
->label('Order #')
->sortable()
->searchable()
->copyable(),
TextColumn::make('status')
->badge()
->sortable(),
TextColumn::make('order_date')
->date()
->sortable(),
TextColumn::make('expected_delivery_date')
->label('Expected Delivery')
->date()
->sortable()
->toggleable(),
TextColumn::make('actual_delivery_date')
->label('Actual Delivery')
->date()
->sortable()
->toggleable(),
TextColumn::make('total_amount')
->label('Total')
->money('USD', divideBy: 100)
->sortable()
->summarize([
Sum::make()
->money('USD', divideBy: 100)
->label('Total Value'),
]),
TextColumn::make('created_at')
->label('Created')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('status')
->options(PurchaseOrderStatus::class)
->multiple(),
Filter::make('delivery_overdue')
->label('Overdue Deliveries')
->query(fn (Builder $query): Builder => $query
->where('status', '!=', PurchaseOrderStatus::Received)
->where('status', '!=', PurchaseOrderStatus::Cancelled)
->where('expected_delivery_date', '<', now())
)
->indicator('Overdue'),
])
->headerActions([
//
])
->recordActions([
EditAction::make()
->after(function(PurchaseOrdersRelationManager $livewire) {
$livewire->dispatch('refresh-page');
}),
Action::make('view')
->label('View Details')
->icon('heroicon-o-eye')
->url(fn ($record) => route('filament.admin.resources.purchase-orders.edit', $record))
->openUrlInNewTab(),
DeleteAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
BulkAction::make('mark_as_received')
->label('Mark as Received')
->icon('heroicon-o-check')
->color('success')
->action(function (array $records) {
collect($records)->each(function ($record) {
$record->update([
'status' => 'received',
'actual_delivery_date' => now(),
]);
});
})
->requiresConfirmation()
->modalHeading('Mark Orders as Received')
->modalDescription('Are you sure you want to mark the selected orders as received?'),
]),
])
->defaultSort('created_at', 'desc')
->emptyStateHeading('No Purchase Orders')
->emptyStateDescription('This supplier has no purchase orders yet.')
->emptyStateIcon('heroicon-o-shopping-cart');
}
}
app/Filament/Resources/Suppliers/Schemas/SupplierSchema.php
<?php
namespace App\Filament\Resources\Suppliers\Schemas;
use App\Enums\PurchaseOrderStatus;
use App\Models\Supplier;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Text;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\HtmlString;
class SupplierSchema
{
public static function configure(Schema $schema): Schema
{
$pendingOrdersQuery = fn(Supplier $record) => $record
->purchaseOrders()
->where('status', PurchaseOrderStatus::Pending);
return $schema
->components([
Section::make(new HtmlString('<span class="text-warning-500 dark:text-warning-400"> Pending Orders </span>'))
->iconColor('warning')
->columnSpanFull()
->icon(Heroicon::OutlinedExclamationTriangle)
->visible(fn(Supplier $record) => $pendingOrdersQuery($record)->exists())
->schema([
Text::make(function (Supplier $record) use ($pendingOrdersQuery) {
$count = $pendingOrdersQuery($record)->count();
return 'There are ' . $count . ' pending order(s) from this supplier.';
}),
]),
Section::make('Basic Information')
->description('Core supplier details and contact information')
->icon('heroicon-o-building-office')
->schema([
TextInput::make('name')
->label('Supplier Name')
->required()
->autofocus()
->maxLength(255)
->placeholder('Enter supplier company name')
->prefixIcon('heroicon-o-building-office-2'),
Toggle::make('is_active')
->label('Active Supplier')
->columnStart(1)
->default(true)
->helperText('Inactive suppliers will not appear in selection lists'),
]),
Section::make('Contact Details')
->description('How to reach this supplier')
->icon('heroicon-o-phone')
->schema([
Grid::make(2)
->schema([
TextInput::make('contact_person')
->label('Primary Contact')
->maxLength(255)
->placeholder('Contact person name')
->prefixIcon('heroicon-o-user'),
TextInput::make('email')
->label('Email Address')
->email()
->maxLength(255)
->placeholder('[email protected]')
->prefixIcon('heroicon-o-envelope'),
]),
Grid::make(2)
->schema([
TextInput::make('phone')
->label('Phone Number')
->tel()
->maxLength(255)
->placeholder('(555) 123-4567')
->prefixIcon('heroicon-o-phone'),
Textarea::make('address')
->label('Business Address')
->maxLength(65535)
->placeholder('Full business address including city, state, and postal code')
->rows(3),
]),
]),
Section::make('Additional Information')
->description('Notes and other relevant details')
->icon('heroicon-o-document-text')
->collapsible()
->schema([
Textarea::make('notes')
->label('Internal Notes')
->maxLength(65535)
->placeholder('Payment terms, delivery preferences, special instructions, etc.')
->rows(4)
->columnSpanFull(),
]),
]);
}
}