F
Filament2mo ago
Gaurav

fillForm on Create Page?

I've created a duplicate action as below:
Tables\Actions\CreateAction::make('Duplicate')
->label('Duplicate')
->icon('heroicon-o-document-duplicate')
->form(fn (Form $form) => static::form($form->model(static::$model))->columns(2))
->fillForm(fn ($record) => $record->toArray()),
Tables\Actions\CreateAction::make('Duplicate')
->label('Duplicate')
->icon('heroicon-o-document-duplicate')
->form(fn (Form $form) => static::form($form->model(static::$model))->columns(2))
->fillForm(fn ($record) => $record->toArray()),
Now, while this works alright for a simple resource (pop-up forms), it doesn't fill the form in case of regular resource (individual create / edit pages). What should I do differently for it to work for regular resource?
Solution:
1. Yea my bad. I was just adopting code from previous messages. You would indeed need to abstract your form fields and reuse them both in your resource and in the replicas form. For example: ```php public static function form(Form $form): Form {...
Jump to solution
21 Replies
toeknee
toeknee2mo ago
$record shouldn't exist in a create action page since no record is on a create page. So you should add a mount method to the create page and use $record = MyModel::find(1); $this->form->fill($record->toArray());
Gaurav
Gaurav2mo ago
But then how do I get the id of the current record to duplicate it?
toeknee
toeknee2mo ago
Why not use replicateAction?
Gaurav
Gaurav2mo ago
That's what I eventually ended up using. Though the reason I didn't want to was that in some cases plain replication would result in duplication of unique required attributes. I'm using simple resource (pop-up forms) in such cases. A better (more consistent) solution would be appreciated though.
toeknee
toeknee2mo ago
So on replicate you can mutate the values too, I also use the after to duplicate relationships too
Gaurav
Gaurav2mo ago
But that value needs to be inputted by the user. That's why I wished to show the record in the form so that user can change the value before saving. Basically it's save as action, which needs to be triggered from the table record (different from Create & create another)
toeknee
toeknee2mo ago
So replicate action with a form?
dissto
dissto2mo ago
ReplicateAction::make()
->form([
TextInput::make('name')
->default(function (Model $record) {
return $record->name;
}),
])
->beforeReplicaSaved(function (Model $replica, array $data): void {
$replica->name = $data['name'] ?? $replica->name;
$replica->save();
}),
ReplicateAction::make()
->form([
TextInput::make('name')
->default(function (Model $record) {
return $record->name;
}),
])
->beforeReplicaSaved(function (Model $replica, array $data): void {
$replica->name = $data['name'] ?? $replica->name;
$replica->save();
}),
You can use a form on the replicate action as well though
Gaurav
Gaurav2mo ago
Wouldn't it be simpler (and more generic) to create a replica action which simply creates a copy of the current record (minus the id perhaps) and show it in create form?
dissto
dissto2mo ago
The id is ommited by default, and all other fields are identical, what do you mean?!
toeknee
toeknee2mo ago
So for that, you would just have a custom action with a form that fills with the current record less the id.
Gaurav
Gaurav2mo ago
I meant a generic replica action which uses form method of the resource (thus available for all resources) Exactly what I was trying to do. The code I had shared in the original post does exactly that in a simple resource, but doesn't work when page change is involved
toeknee
toeknee2mo ago
Action::make('replicate_form)->form(self::form())->fillForm(fn($record) => $record)
Gaurav
Gaurav2mo ago
So, my mistake is only that I've used CreateAction? I believe there will be a couple of more steps required in case of a generic action, such as letting filament know what to do when the form has been filled, via some action buttons. May as well need to create a separate page for this, similar to edit page.
toeknee
toeknee2mo ago
Yep that would be
->action(function($data) {

})
->action(function($data) {

})
dissto
dissto2mo ago
Well. In that case you could still use the ReplicateAction and safe a bunch of extra work 😋
ReplicateAction::make()
->form(self::form())
->fillForm(fn ($record) => $record->toArray())
->beforeReplicaSaved(function (Model $replica, array $data): void {
foreach ($replica->getAttributes() as $key => $value) {
$replica->{$key} = $data[$key] ?? $value;
}
$replica->save();
})
ReplicateAction::make()
->form(self::form())
->fillForm(fn ($record) => $record->toArray())
->beforeReplicaSaved(function (Model $replica, array $data): void {
foreach ($replica->getAttributes() as $key => $value) {
$replica->{$key} = $data[$key] ?? $value;
}
$replica->save();
})
toeknee
toeknee2mo ago
Good shout
Gaurav
Gaurav2mo ago
Let me try this and get back 2 things: 1. form(self::form()) doesn't work as self::form requires a from argument. So, I've used form(fn (Form $form) => static::form($form->model(static::$model))->columns(2)) instead 2. It creates a new record with old values, and updated the existing record with new values
Solution
dissto
dissto2mo ago
1. Yea my bad. I was just adopting code from previous messages. You would indeed need to abstract your form fields and reuse them both in your resource and in the replicas form. For example:
public static function form(Form $form): Form
{
return $form
->schema(static::getFormSchemaContent());
}
public static function form(Form $form): Form
{
return $form
->schema(static::getFormSchemaContent());
}
public static function getFormSchemaContent(): array
{
return [
TextInput::make('dummy'),
Select::make('another_dummy')
];
}
public static function getFormSchemaContent(): array
{
return [
TextInput::make('dummy'),
Select::make('another_dummy')
];
}
Also see: https://filamentphp.com/docs/3.x/panels/resources/creating-records#sharing-fields-between-the-resource-form-and-wizards Then you could use
ReplicateAction::make()
->form(static::getFormSchemaContent())
->fillForm(fn ($record) => $record->toArray())
->beforeReplicaSaved(function (Model $replica, array $data): void {
foreach ($replica->getAttributes() as $key => $value) {
$replica->{$key} = $data[$key] ?? $value;
}
$replica->save();
})
ReplicateAction::make()
->form(static::getFormSchemaContent())
->fillForm(fn ($record) => $record->toArray())
->beforeReplicaSaved(function (Model $replica, array $data): void {
foreach ($replica->getAttributes() as $key => $value) {
$replica->{$key} = $data[$key] ?? $value;
}
$replica->save();
})
2. I suppose that is related to 1 because form(fn (Form $form) => static::form($form->model(static::$model))->columns(2)) seems rather unconventional. Not sure where you got that from 🤔
Gaurav
Gaurav2mo ago
I will give it a try and report back This works, just made a little change to the beforeReplicaSaved closure as:
->beforeReplicaSaved(function (Model $replica, array $data): void {
$replica->setRawAttributes($data);
}
->beforeReplicaSaved(function (Model $replica, array $data): void {
$replica->setRawAttributes($data);
}
I am not explicitly calling save as that shall be done by the action itself