Filament v3中Repeater的afterStateUpdate无法更新外部字段
问题:Filament v3订单表单Repeater组件无法同步更新外部字段
使用Filament v3开发订单表单时,通过Repeater组件关联Product表,需求是选择产品或调整数量后,Repeater外部的subtotal和total字段同步更新。目前Repeater的afterStateUpdate方法仅能更新内部字段,外部字段无变化。
相关代码
Repeater组件代码
Repeater::make('products') ->relationship() ->schema([ Select::make('product_id') ->label("Products") ->relationship('product','name') ->options( $products->mapWithKeys(function (Product $product) { return [$product->id => sprintf('%s ($%s)', $product->name, $product->price)]; }) ) ->disableOptionWhen(function ($value, $state, Get $get) { return collect($get('../*.product_id')) ->reject(fn($id) => $id == $state) ->filter() ->contains($value); }) ->required() ->createOptionForm([ Forms\Components\FileUpload::make('image') ->image() ->required(), Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Forms\Components\RichEditor::make('description') ->required() ->maxLength(65535) ->columnSpanFull(), Forms\Components\TextInput::make('sku') ->label('SKU') ->required() ->maxLength(255), Forms\Components\TextInput::make('price') ->required() ->maxLength(255) ->numeric(), Forms\Components\TextInput::make('qty') ->required() ->maxLength(255) ->numeric(), Forms\Components\Select::make('availablity') ->options([ "yes" => "Yes", 'no' => "No" ]) ->required(), Forms\Components\Select::make('brands') ->relationship('brands', 'name') ->required() ->multiple() ->preload() ->createOptionForm([ Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Forms\Components\Toggle::make('active') ->required(), ]), Forms\Components\Select::make('categories') ->relationship('categories', 'name') ->required() ->multiple() ->preload() ->createOptionForm([ Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Forms\Components\TextInput::make('description') ->required() ->maxLength(255), Forms\Components\Toggle::make('active') ->required(), ]), Forms\Components\Select::make('warehouse_id') ->relationship('warehouse', 'name') ->required() ->createOptionForm([ Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Toggle::make('active') ]) ]) ->columnSpan([ "md" => 5 ]), TextInput::make('amount') ->numeric() ->required() ->reactive() ->label('Quantity') ->default(1) ->columnSpan([ "md" => 3 ]), TextInput::make('price') ->disabled() ->numeric() ->minValue('1') ->required() ->default(1) ->dehydrated() ->columnSpan([ "md" => 2 ]), ])->columns(["md" => 10]) ->live() ->afterStateUpdated(fn ($state) => dd($state)) ->deleteAction( fn(Action $action) => $action->after(fn(Get $get, Set $set) => self::updateTotals($get, $set)), ) ->reorderable(false) ])->collapsible(),
外部区域代码
Section::make() ->columns(1) ->maxWidth('1/2')->schema([ TextInput::make('subtotal') ->numeric() ->readOnly() ->afterStateHydrated(function (Get $get, Set $set) { self::updateTotals($get, $set); }), TextInput::make('total')->readOnly()->numeric() ])
updateTotals函数代码
public static function updateTotals(Get $get, Set $set): void { // 筛选出已选择的有效产品行 $selectedProducts = collect($get('products'))->filter(fn($item) => !empty($item['product_id']) && !empty($item['amount'])); // 获取选中产品的价格映射 $prices = Product::find($selectedProducts->pluck('product_id'))->pluck('price', 'id'); // 计算小计 $subtotal = $selectedProducts->reduce(function ($subtotal, $product) use ($prices) { return $subtotal + ($prices[$product['product_id']] * $product['amount']); }, 0); // 更新小计和总计字段 $set('subtotal', number_format($subtotal, 2, '.', '')); $set('total', number_format($subtotal + ($subtotal * ($get('taxes') / 100)), 2, '.', '')); }
解决方案
问题核心是Repeater内部字段变更时未触发外部总计更新逻辑,且Repeater的状态更新回调未正确执行计算。
启用内部字段响应式并触发更新
给product_id和amount添加reactive(),并在afterStateUpdated中同时更新当前行价格和外部总计:// 给product_id字段添加 ->reactive() ->afterStateUpdated(function (Get $get, Set $set) { $product = Product::find($get('product_id')); if ($product) { $set('price', $product->price * $get('amount')); } self::updateTotals($get, $set); }) // 给amount字段添加 ->reactive() ->afterStateUpdated(function (Get $get, Set $set) { $product = Product::find($get('product_id')); if ($product) { $set('price', $product->price * $get('amount')); } self::updateTotals($get, $set); })修正Repeater的afterStateUpdated回调
移除调试用的dd($state),改为调用总计更新函数,确保新增行后触发计算:->live() ->afterStateUpdated(function (Get $get, Set $set) { self::updateTotals($get, $set); })处理taxes字段空值问题
在updateTotals中给taxes设置默认值,避免除以0错误:$taxRate = $get('taxes') ?? 0; $set('total', number_format($subtotal + ($subtotal * ($taxRate / 100)), 2, '.', ''));
内容的提问来源于stack exchange,提问作者Muhammad Fareed Khan
相关产品推荐
相关产品推荐

