F
Filament10mo ago
prowler

Repeater acts differently when used inside createOptionForm

Hello everyone, so here's the situation - I have a ComparisonTableResource which basically lets the user build comparison tables on the frontent and includes a list of Operators (a different model) and lets the user change their order in such way -
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
It works just fine and behaves as expected, including saving the correct order inside the pivot table between Operator and ComparisonTable (comparison_table_operator). Now, I have a different model and a resource called Lander in which the user can choose an existing comparison table or create a new one, like this (in LanderResource.php) -
Select::make('comparison_tables_id')
->relationship('comparison_table', 'title')
->createOptionForm([

Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]),
...
Select::make('comparison_tables_id')
->relationship('comparison_table', 'title')
->createOptionForm([

Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]),
...
Now, when simply selecting an existing comparison table - it works just fine, but when creating a new one and selecting the operators in the modal form, i get an error. saying SQLSTATE[HY000]: General error: 1364 Field 'name' doesn't have a default value.
7 Replies
prowler
prowlerOP10mo ago
The error then followed by this line -
INSERT INTO
`operators` (
`is_current`,
`publisher_id`,
`publisher_type`,
`uuid`,
`published_at`,
`is_published`,
`updated_at`,
`created_at`
)
VALUES
(
1,
1,
App \ Models \ USER,
301c4402 - 14bc -4403 - 91dd - 46163cfdd1e5,
2024 -02 -06 16: 52: 36,
1,
2024 -02 -06 16: 52: 36,
2024 -02 -06 16: 52: 36
)`
INSERT INTO
`operators` (
`is_current`,
`publisher_id`,
`publisher_type`,
`uuid`,
`published_at`,
`is_published`,
`updated_at`,
`created_at`
)
VALUES
(
1,
1,
App \ Models \ USER,
301c4402 - 14bc -4403 - 91dd - 46163cfdd1e5,
2024 -02 -06 16: 52: 36,
1,
2024 -02 -06 16: 52: 36,
2024 -02 -06 16: 52: 36
)`
Like its trying to insert a new operator?.. but why? Due to character limitations, here are the full resources for a better understanding -
<?php

namespace App\Filament\Resources;

class ComparisonTableResource extends Resource
{

public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->live(onBlur: true)
->afterStateUpdated(function (Set $set, $state) {
$set('slug', Str::slug($state));
})
->maxLength(255),
Forms\Components\TextInput::make('slug')
->maxLength(255),
ColorPicker::make('color')->label('Main Color'),
ColorPicker::make('winner_color')->label('Winner Color'),
ColorPicker::make('button_color')->label('CTA Color'),

Forms\Components\TextInput::make('excerpt')
->maxLength(255),
Select::make('type')->options(ComparisonTableTypeEnum::class),
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]);
}
}
<?php

namespace App\Filament\Resources;

class ComparisonTableResource extends Resource
{

public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->live(onBlur: true)
->afterStateUpdated(function (Set $set, $state) {
$set('slug', Str::slug($state));
})
->maxLength(255),
Forms\Components\TextInput::make('slug')
->maxLength(255),
ColorPicker::make('color')->label('Main Color'),
ColorPicker::make('winner_color')->label('Winner Color'),
ColorPicker::make('button_color')->label('CTA Color'),

Forms\Components\TextInput::make('excerpt')
->maxLength(255),
Select::make('type')->options(ComparisonTableTypeEnum::class),
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]);
}
}
And here's the resource for Lander
<?php

namespace App\Filament\Resources;


class LanderResource extends Resource
{


public static function form(Form $form): Form
{
return $form
->schema([
Select::make('comparison_tables_id')
->relationship('comparison_table', 'title')
->createOptionForm([
Forms\Components\TextInput::make('title')
->live(onBlur: true)
->afterStateUpdated(function (Set $set, $state) {
$set('slug', Str::slug($state));
})
->maxLength(255),
Forms\Components\TextInput::make('slug')
->maxLength(255),
ColorPicker::make('color')->label('Main Color'),
ColorPicker::make('winner_color')->label('Winner Color'),
ColorPicker::make('button_color')->label('CTA Color'),

Forms\Components\TextInput::make('excerpt')
->maxLength(255),
Select::make('type')->options(ComparisonTableTypeEnum::class),
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]),
// more fields...
]);
}

}
<?php

namespace App\Filament\Resources;


class LanderResource extends Resource
{


public static function form(Form $form): Form
{
return $form
->schema([
Select::make('comparison_tables_id')
->relationship('comparison_table', 'title')
->createOptionForm([
Forms\Components\TextInput::make('title')
->live(onBlur: true)
->afterStateUpdated(function (Set $set, $state) {
$set('slug', Str::slug($state));
})
->maxLength(255),
Forms\Components\TextInput::make('slug')
->maxLength(255),
ColorPicker::make('color')->label('Main Color'),
ColorPicker::make('winner_color')->label('Winner Color'),
ColorPicker::make('button_color')->label('CTA Color'),

Forms\Components\TextInput::make('excerpt')
->maxLength(255),
Select::make('type')->options(ComparisonTableTypeEnum::class),
Repeater::make('operators')
->relationship('operators')
->schema([
Select::make('operator_id')
->label('Operator')
->options(\App\Models\Operator::all()->pluck('name', 'id')),
])
->reorderable()
->columnSpanFull(),
]),
// more fields...
]);
}

}
Anyone, please?
awcodes
awcodes10mo ago
On my phone, so having trouble reasoning all the code, but I’m wondering if it has something to do with the repeater having the same name as the relationship.
prowler
prowlerOP10mo ago
nope.. tried renaming it with no success. the weird thing is that its working on the ComparisonTableResource as is (with the same name for both repeater as the relationship), but when used inside a modal with createOptionFrom it tries to insert new Operator entries for some reason.. also, for some reason, the repeater order is only being saved when a new entry is created .. when im editing an existing ComparisonTable and simply change the order of the Operators - it doesn't update it at all. Here's my CreateComparisonTable.php file to handle the custom order -
class CreateComparisonTable extends CreateRecord
{
protected static string $resource = ComparisonTableResource::class;

protected array $operatorIds = [];

protected function beforeCreate(): void
{
if (isset($this->data['operators'])) {
$this->operatorIds = [];
$index = 0;

foreach ($this->data['operators'] as $item) {
if (isset($item['operator_id'])) {
$this->operatorIds[$item['operator_id']] = ['order' => $index++];
}
}

unset($this->data['operators']);
}
}

protected function afterCreate(): void
{
if (! empty($this->operatorIds)) {
$this->record->operators()->sync($this->operatorIds);
}
}
}
class CreateComparisonTable extends CreateRecord
{
protected static string $resource = ComparisonTableResource::class;

protected array $operatorIds = [];

protected function beforeCreate(): void
{
if (isset($this->data['operators'])) {
$this->operatorIds = [];
$index = 0;

foreach ($this->data['operators'] as $item) {
if (isset($item['operator_id'])) {
$this->operatorIds[$item['operator_id']] = ['order' => $index++];
}
}

unset($this->data['operators']);
}
}

protected function afterCreate(): void
{
if (! empty($this->operatorIds)) {
$this->record->operators()->sync($this->operatorIds);
}
}
}
I tried to mimic the same behvaiour in EditComparisonTable.php inside the handleRecordUpdate() function, with no success. any idea, @awcodes ? I hava a comparison_table_operator pivot table which simply should hold table ids, operators ids and their order in those tables, like this -
CREATE TABLE `comparison_table_operator` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comparison_table_id` bigint unsigned NOT NULL,
`operator_id` bigint unsigned NOT NULL,
`order` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `comparison_table_operator_unique` (`comparison_table_id`,`operator_id`),
KEY `comparison_table_operator_operator_id_foreign` (`operator_id`),
CONSTRAINT `comparison_table_operator_comparison_table_id_foreign` FOREIGN KEY (`comparison_table_id`) REFERENCES `comparison_tables` (`id`) ON DELETE CASCADE,
CONSTRAINT `comparison_table_operator_operator_id_foreign` FOREIGN KEY (`operator_id`) REFERENCES `operators` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `comparison_table_operator` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comparison_table_id` bigint unsigned NOT NULL,
`operator_id` bigint unsigned NOT NULL,
`order` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `comparison_table_operator_unique` (`comparison_table_id`,`operator_id`),
KEY `comparison_table_operator_operator_id_foreign` (`operator_id`),
CONSTRAINT `comparison_table_operator_comparison_table_id_foreign` FOREIGN KEY (`comparison_table_id`) REFERENCES `comparison_tables` (`id`) ON DELETE CASCADE,
CONSTRAINT `comparison_table_operator_operator_id_foreign` FOREIGN KEY (`operator_id`) REFERENCES `operators` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
when i tried to simply dd($data) inside the mutateFormDataBeforeSave function - i noticed that the repeater fields array wasn't even there.
awcodes
awcodes10mo ago
Do you have a repo you could share. I would need to step through it to see what is happening.
prowler
prowlerOP10mo ago
sadly not atm. its a private repo.. is there any additional info i can provide or drop some dd()s here n' there?
awcodes
awcodes10mo ago
I’m on my phone at the moment so it’s really difficult to follow all the code.
prowler
prowlerOP10mo ago
ok, thanks for your time and effort..
Want results from more Discord servers?
Add your server