F
Filament5mo ago
Tieme

2 "save" actions on CreateResource with "mutateFormDataBeforeCreate"

Hi All, I'm aware that actions can be added to a Resource. I would like to add an additional "Save" button on the CreateResource page. My goal is to mutate one parameter (e.g., $data['status'] = InternalStatus::CONCEPT) only when this new button is pressed. Does anyone have any ideas on how to achieve this? Thanks in advance!
Solution:
Here is the solution. Create a Formfield, hide the label and add hidden as extraAttributes ```php...
Jump to solution
8 Replies
Dennis Koch
Dennis Koch5mo ago
Implement getFormActions() on the CreatePage. See the original implementation for reference
Tieme
TiemeOP5mo ago
I have that implemented, But don't know how to mutate the data for the "getCreateConceptFormAction"
protected function getFormActions(): array
{
return [
$this->getCreateFormAction()
->keyBindings(false)
->requiresConfirmation()
->label(Helpers::translate('Place Order')),
$this->getCreateConceptFormAction(), // Todo :: figure out how to do this.
$this->getCancelFormAction(),
];
}

// Todo : not working...
protected function getCreateConceptFormAction(): Action
{
return Action::make('createConcept')
->label('Create Concept')
->action(function (array $data) {
$data = $this->mutateFormDataBeforeCreate($data);
$data['status'] = InternalProjectStatus::CONCEPT->value;
dd($data);
static::getModel()::create($data);
})
->model(static::getModel())
->form(fn ($livewire) => static::getResource()::form(new Form($livewire)));

// return Action::make('create')
// //->label(__('filament-panels::resources/pages/create-record.form.actions.create.label'))
// ->label(Helpers::translate('Save as concept'))
// ->color(Color::Blue->getColor())
// ->submit('create')
// ->successNotification(
// Notifications\Notification::make()
// ->success()
// ->title('Order saves as concept'),
// )
// ->keyBindings(['mod+s']);
}
protected function getFormActions(): array
{
return [
$this->getCreateFormAction()
->keyBindings(false)
->requiresConfirmation()
->label(Helpers::translate('Place Order')),
$this->getCreateConceptFormAction(), // Todo :: figure out how to do this.
$this->getCancelFormAction(),
];
}

// Todo : not working...
protected function getCreateConceptFormAction(): Action
{
return Action::make('createConcept')
->label('Create Concept')
->action(function (array $data) {
$data = $this->mutateFormDataBeforeCreate($data);
$data['status'] = InternalProjectStatus::CONCEPT->value;
dd($data);
static::getModel()::create($data);
})
->model(static::getModel())
->form(fn ($livewire) => static::getResource()::form(new Form($livewire)));

// return Action::make('create')
// //->label(__('filament-panels::resources/pages/create-record.form.actions.create.label'))
// ->label(Helpers::translate('Save as concept'))
// ->color(Color::Blue->getColor())
// ->submit('create')
// ->successNotification(
// Notifications\Notification::make()
// ->success()
// ->title('Order saves as concept'),
// )
// ->keyBindings(['mod+s']);
}
Dennis Koch
Dennis Koch5mo ago
That's why I said "see the original implementation". It doesn't have a ->action() method: This is the original code:
Action::make('create')
->label(__('filament-panels::resources/pages/create-record.form.actions.create.label'))
->submit('create')
->keyBindings(['mod+s']);
Action::make('create')
->label(__('filament-panels::resources/pages/create-record.form.actions.create.label'))
->submit('create')
->keyBindings(['mod+s']);
And this calls the create() method on the CreateRecord class.
Tieme
TiemeOP5mo ago
So i need to duplicate the create function to only change 1 attribute that needs to be changed? Just like below where i added the $data['status'] = InternalProjectStatus::CONCEPT->value; Isent there a better way to do this?
Action::make('createConcept')
->label('Create Concept')
->submit('createConcept')
->keyBindings(['mod+s']);

public function createConcept(bool $another = false): void
{
$this->authorizeAccess();

try {
$this->beginDatabaseTransaction();

$this->callHook('beforeValidate');

$data = $this->form->getState();

$this->callHook('afterValidate');

$data = $this->mutateFormDataBeforeCreate($data);
$data['status'] = InternalProjectStatus::CONCEPT->value;

$this->callHook('beforeCreate');

$this->record = $this->handleRecordCreation($data);

$this->form->model($this->getRecord())->saveRelationships();

$this->callHook('afterCreate');

$this->commitDatabaseTransaction();
} catch (Halt $exception) {
$exception->shouldRollbackDatabaseTransaction() ?
$this->rollBackDatabaseTransaction() :
$this->commitDatabaseTransaction();

return;
} catch (Throwable $exception) {
$this->rollBackDatabaseTransaction();

throw $exception;
}

$this->rememberData();

$this->getCreatedNotification()?->send();

if ($another) {
// Ensure that the form record is anonymized so that relationships aren't loaded.
$this->form->model($this->getRecord()::class);
$this->record = null;

$this->fillForm();

return;
}

$redirectUrl = $this->getRedirectUrl();

$this->redirect($redirectUrl, navigate: FilamentView::hasSpaMode() && is_app_url($redirectUrl));
}
Action::make('createConcept')
->label('Create Concept')
->submit('createConcept')
->keyBindings(['mod+s']);

public function createConcept(bool $another = false): void
{
$this->authorizeAccess();

try {
$this->beginDatabaseTransaction();

$this->callHook('beforeValidate');

$data = $this->form->getState();

$this->callHook('afterValidate');

$data = $this->mutateFormDataBeforeCreate($data);
$data['status'] = InternalProjectStatus::CONCEPT->value;

$this->callHook('beforeCreate');

$this->record = $this->handleRecordCreation($data);

$this->form->model($this->getRecord())->saveRelationships();

$this->callHook('afterCreate');

$this->commitDatabaseTransaction();
} catch (Halt $exception) {
$exception->shouldRollbackDatabaseTransaction() ?
$this->rollBackDatabaseTransaction() :
$this->commitDatabaseTransaction();

return;
} catch (Throwable $exception) {
$this->rollBackDatabaseTransaction();

throw $exception;
}

$this->rememberData();

$this->getCreatedNotification()?->send();

if ($another) {
// Ensure that the form record is anonymized so that relationships aren't loaded.
$this->form->model($this->getRecord()::class);
$this->record = null;

$this->fillForm();

return;
}

$redirectUrl = $this->getRedirectUrl();

$this->redirect($redirectUrl, navigate: FilamentView::hasSpaMode() && is_app_url($redirectUrl));
}
Dennis Koch
Dennis Koch5mo ago
How about this:
public function createConcept(bool $another = false): void
{
$this->mutateFormDataBeforeCreate(fn ($data) => /* your mutation */)
$this->create($another);
}
public function createConcept(bool $another = false): void
{
$this->mutateFormDataBeforeCreate(fn ($data) => /* your mutation */)
$this->create($another);
}
Tieme
TiemeOP5mo ago
Damn, did not think about that! Thanks, that will do the trick!
Solution
Tieme
Tieme5mo ago
Here is the solution. Create a Formfield, hide the label and add hidden as extraAttributes
Forms\Components\ToggleButtons::make('status')
->options(InternalProjectStatus::class)
->label(false)
->dehydrated(true)
->columnSpan('full')
->default(fn () => InternalProjectStatus::LOCKED)
->extraAttributes([
'class' => 'hidden',
]),
Forms\Components\ToggleButtons::make('status')
->options(InternalProjectStatus::class)
->label(false)
->dehydrated(true)
->columnSpan('full')
->default(fn () => InternalProjectStatus::LOCKED)
->extraAttributes([
'class' => 'hidden',
]),
For the action, set submit to null and update the $livewire->data['status'] in action and than create the record. This will set status to Concept in my case.
protected function getCreateConceptFormAction(): Action
{
return Action::make('concept')
->label(Helpers::translate('Save as concept'))
->color(Color::Blue->getColor())
->submit(null)
->action(function ($livewire) {
$livewire->data['status'] = InternalProjectStatus::CONCEPT;
$this->create();
})
->successNotification(
Notifications\Notification::make()
->success()
->title('Order saves as concept'),
)
->keyBindings(['mod+s']);
}
protected function getCreateConceptFormAction(): Action
{
return Action::make('concept')
->label(Helpers::translate('Save as concept'))
->color(Color::Blue->getColor())
->submit(null)
->action(function ($livewire) {
$livewire->data['status'] = InternalProjectStatus::CONCEPT;
$this->create();
})
->successNotification(
Notifications\Notification::make()
->success()
->title('Order saves as concept'),
)
->keyBindings(['mod+s']);
}
If there is another way i would love to see it. Because this works, but dont think this is the best way of doing this.
Dennis Koch
Dennis Koch5mo ago
Thanks for sharing.
Want results from more Discord servers?
Add your server