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
toeknee7mo 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());
Gandalf
GandalfOP7mo ago
But then how do I get the id of the current record to duplicate it?
toeknee
toeknee7mo ago
Why not use replicateAction?
Gandalf
GandalfOP7mo 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
toeknee7mo ago
So on replicate you can mutate the values too, I also use the after to duplicate relationships too
Gandalf
GandalfOP7mo 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
toeknee7mo ago
So replicate action with a form?
dissto
dissto7mo 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
Gandalf
GandalfOP7mo 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
dissto7mo ago
The id is ommited by default, and all other fields are identical, what do you mean?!
toeknee
toeknee7mo ago
So for that, you would just have a custom action with a form that fills with the current record less the id.
Gandalf
GandalfOP7mo 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
toeknee7mo ago
Action::make('replicate_form)->form(self::form())->fillForm(fn($record) => $record)
Gandalf
GandalfOP7mo 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
toeknee7mo ago
Yep that would be
->action(function($data) {

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

})
dissto
dissto7mo 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
toeknee7mo ago
Good shout
Gandalf
GandalfOP7mo 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
dissto7mo 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 🤔
Gandalf
GandalfOP7mo 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
Want results from more Discord servers?
Add your server