Nesting repeaters for BelongsToMany relationships
marc31 opened this issue · comments
Package
filament/filament
Package Version
v3.2
Laravel Version
v11.9
Livewire Version
v3.5
PHP Version
PHP 8.3
Problem description
Following the example of using a repeater to fill BelongsToMany relationships [https://filamentphp.com/docs/3.x/forms/fields/repeater], I want to nest two repeaters with BelongsToMany relationships to handle a scenario like this:
I have Orders that have Products, which in turn have Colors.
I'm encountering a bug during the initial save: the color is not saved correctly because the order_product_id column in the color_order_product table is null. However, if I add new colors to a product in the order afterward, the modification is saved correctly.
But if I add a new product, the same issue arises.
Repeater::make('order_product')
->relationship('orderProducts')
->schema([
Select::make('product_id')
->relationship('product', 'title')
->required()
->live(),
Repeater::make('colorOrderProducts')
->relationship('colorOrderProducts')
->schema([
Select::make('color_id')
->options(
function ($get) {
$selectedProduct = Product::find($get('../../product_id'));
if ($selectedProduct) {
return $selectedProduct->colors()->get()->pluck('title', 'id');
}
}
)
->required(),
])
])
Expected behavior
When creating an order, adding products, and assigning colors to those products using the nested repeater, all entries should be saved correctly.
Steps to reproduce
-
Create an order with products and assign colors to those products using the nested repeater.
-
Save the order.
-
Check the color_order_product table and observe that the order_product_id column is null for the initially added colors.
-
Add new colors to the existing product and save again. Observe that these new colors are saved correctly.
-
Add a new product to the order and assign colors. Save and observe the issue reoccurs.
Reproduction repository
https://github.com/marc31/filament-nested-repeater-w-relationship
Relevant log output
No response
Donate 💰 to fund this issue
- You can donate funding to this issue. We receive the money once the issue is completed & confirmed by you.
- 100% of the funding will be distributed between the Filament core team to run all aspects of the project.
- Thank you in advance for helping us make maintenance sustainable!
that was the same for me if you have a solution for this ,pldas
My Resource
<?php
namespace App\Filament\Resources;
use App\Filament\Enums\AdvantageLayoutTypeEnum;
use App\Filament\Resources\ProductCategoryResource\Pages;
use App\Models\ProductCategory;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Concerns\Translatable;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Support\Str;
class ProductCategoryResource extends Resource
{
use Translatable;
protected static ?string $model = ProductCategory::class;
protected static ?string $navigationIcon = 'heroicon-o-queue-list';
protected static ?string $navigationGroup = 'Manage Products';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255)
->live(onBlur: true)
->afterStateUpdated(function (string $operation, $state, Forms\Set $set): void {
if ($operation !== 'create') {
return;
}
$set('slug', Str::slug($state));
}),
Forms\Components\TextInput::make('slug')
->disabled()
->dehydrated()
->required()
->maxLength(255)
->unique(ProductCategory::class, 'slug', ignoreRecord: true),
Forms\Components\FileUpload::make('icon')
->image(),
Forms\Components\FileUpload::make('image')
->image(),
Forms\Components\Repeater::make('advantages')
->schema([
Forms\Components\TextInput::make('title')
->required(),
Forms\Components\Repeater::make('contents')
->schema([
Forms\Components\TextInput::make('order')
->numeric()
->required(),
Forms\Components\TextInput::make('title')
->required(),
Forms\Components\FileUpload::make('img')
->disk('public')
->directory('advantages')
->image(),
Forms\Components\MarkdownEditor::make('desc')
->required(),
Forms\Components\Select::make('type')
->options(AdvantageLayoutTypeEnum::class)
->required(),
])
->minItems(1)
->collapsible()
->required(),
])
->minItems(1)
->addable(false)
->deletable(false)
->required()
->columnSpanFull(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
->searchable(),
Tables\Columns\TextColumn::make('slug')
->searchable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\ImageColumn::make('icon'),
Tables\Columns\ImageColumn::make('image'),
// Tables\Columns\TextColumn::make('advantages.title')
// ->searchable(),
Tables\Columns\TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListProductCategories::route('/'),
'create' => Pages\CreateProductCategory::route('/create'),
'edit' => Pages\EditProductCategory::route('/{record}/edit'),
];
}
}
my migration
Schema::create('product_categories', function (Blueprint $table): void {
$table->id();
$table->text('name');
$table->string('slug')->unique();
$table->string('icon')->nullable()->default(null);
$table->string('image')->nullable()->default(null);
$table->json('advantages')->nullable();
$table->timestamps();
});
my seeder
$category = ProductCategory::create([
'name' => $categoriesName,
'slug' => Str::slug($categoriesName),
'icon' => 'advantages/01HZE5794C7ADXYKC9HPXHFHS2.png',
'image' => 'advantages/01HZE5794A1KQQYGAQQ9V7Y8B6.png',
'advantages' => [
[
'title' => 'Advantages of Bio Stimulant',
'contents' => [
[
'order' => 1,
'title' => 'BioStimulants Assits In Combating The Effects Of Enveromental Stresses',
'img' => 'advantages/01HZE5794A1KQQYGAQQ9V7Y8B6.png',
'desc' => fake()->paragraph(),
'type' => 'right section',
], [
'order' => 2,
'title' => 'Qui aute corporis no',
'img' => 'advantages/01HZE5794C7ADXYKC9HPXHFHS2.png',
'desc' => fake()->paragraph(),
'type' => 'left section',
],
[
'order' => 3,
'title' => 'Advantage 1',
'img' => 'advantage1.png',
'desc' => fake()->paragraph(),
'type' => 'right section',
],
[
'order' => 4,
'title' => 'Advantage 2',
'img' => 'advantage2.png',
'desc' => fake()->paragraph(),
'type' => 'final section',
],
],
],
],
]);