External validation and state path...

I believe I've encountered this before and I have searched discord, but to no avail. I am using Form Builder and in one form I'm calling an external class to update a model on "save". The external class performs the validation, but it's not "registering" with the filament/livewire component when it fails because I am scoping the form data to an array property...a la https://filamentphp.com/docs/2.x/forms/getting-started#scoping-form-data-to-an-array-property. Can anyone point me in the right direction on what to do?
Filament
Getting started - Form Builder - Filament
The elegant TALL stack form builder for Laravel artisans.
19 Replies
LeandroFerreira
Can you share the code please?
Travis
TravisOP2y ago
Thx...! 🤓 Here it is, with some minor modifications:
class GeneralSettingsForm extends Component implements HasForms
{
use HasUserProperty;
use InteractsWithForms;

public array $data;

public MyModel $myModel;

public function mount(): void
{
$this->form->fill($this->myModel->attributesToArray());
}

protected function getFormStatePath(): string
{
return 'data';
}

public function saveSettings(UpdateMyModel $updateMyModel)
{
$this->resetErrorBag();

// **this** is where the validation occurs...inside this "action class"...
$updateMyModel($this->myModel, $this->form->getState());

Notification::make()
->title('Saved')
->success()
->send();
}

protected function getFormSchema(): array
{
return [
$this->bioField(),
$this->isSearchableField(),
];
}

private function bioField()
{
return Forms\Components\MarkdownEditor::make(('bio'));
}

private function isSearchableField()
{
return Forms\Components\Toggle::make(('is_searchable'));
}

public function render(): View
{
return view('livewire.seeker-profile.general-settings-form');
}
}
class GeneralSettingsForm extends Component implements HasForms
{
use HasUserProperty;
use InteractsWithForms;

public array $data;

public MyModel $myModel;

public function mount(): void
{
$this->form->fill($this->myModel->attributesToArray());
}

protected function getFormStatePath(): string
{
return 'data';
}

public function saveSettings(UpdateMyModel $updateMyModel)
{
$this->resetErrorBag();

// **this** is where the validation occurs...inside this "action class"...
$updateMyModel($this->myModel, $this->form->getState());

Notification::make()
->title('Saved')
->success()
->send();
}

protected function getFormSchema(): array
{
return [
$this->bioField(),
$this->isSearchableField(),
];
}

private function bioField()
{
return Forms\Components\MarkdownEditor::make(('bio'));
}

private function isSearchableField()
{
return Forms\Components\Toggle::make(('is_searchable'));
}

public function render(): View
{
return view('livewire.seeker-profile.general-settings-form');
}
}
You can see inside the saveSettings() method...I receive an invokable "action class" instance that I execute, updating my model. It is responsible for validation. It correctly validates the input data, but when it throws, it's "out of sync" with the state path. Hopefully that makes sense.....
LeandroFerreira
what about throw an exception? Something like this:
throw_if(
$condition,
ValidationException::withMessages(['field_name' => 'error message'])
);
throw_if(
$condition,
ValidationException::withMessages(['field_name' => 'error message'])
);
Dan Harrin
Dan Harrin2y ago
i suggest that action classes are never responsible for validation i think that is an antipattern, since the validation is not actually part of the action itself, and is often very dependant on the context of when the action is dispatched (http, job, lw, command) it causes more problems than it solves in my experience
Travis
TravisOP2y ago
Thank you...but I'm trying to find a way to centralize the validation rules.
Dan Harrin
Dan Harrin2y ago
filament validation rules will always be separate from your other rules, there isnt a way to get around that
Travis
TravisOP2y ago
Do you have any suggestions, then, on how to keep the validation rules DRY?
Dan Harrin
Dan Harrin2y ago
where else are you using validation rules?
Travis
TravisOP2y ago
OK. I get it. I just don't want it to be that way. 😅
Dan Harrin
Dan Harrin2y ago
DRY is fine, but also WET (write everything twice). these are different situations, we provide frontend validation in some cases i dont think its a bad thing to repeat validation rules
Travis
TravisOP2y ago
API...and slightly different rules between creates/updates....similar to what you mentioned already about differences given the context. OK. As for "mapping" the state path...I'm guessing that's also not possible...?
Dan Harrin
Dan Harrin2y ago
i dont really know what you mean by that
Travis
TravisOP2y ago
I am scoping the form data to an array called $data....like in the docs. When the validation occurs, it doesn't pass data.first_name, but just first_name, so when the validation exception occurs, the invalid keys are lacking the data.* prefix or whatever and so the form won't update and correctly show errors. I could swear I was pointed to a solution for this in the docs some time back....but I'm starting to think that I may have imagined that.
Dan Harrin
Dan Harrin2y ago
where are you seeing that it doesnt pass data.?
Travis
TravisOP2y ago
As you know, I pass $this->form->getState() to the action class, which it uses as input for the validation. I dumped it there when testing to see....
Dan Harrin
Dan Harrin2y ago
this isnt a problem, the data has already been validated when you call getState() noone else has any need for a data. prefix there, it would annoy them in any other situation this is a sensible default IMO for people who are using the form builder as we intend it to be used if you fall outside the intended use case for the package, then you will need to work out how to hack it to make it work im sorry, but if everyone did it differently then we would have a support nightmare
Travis
TravisOP2y ago
I think I'll just take you up on your advice/recommendation. I like to try to keep things simple....and it seems that while I was trying to simplify one thing, I complicated another....
Dan Harrin
Dan Harrin2y ago
yeah, keep it simple 👍 validation is very contextual it always depends on what is passed in, who is doing it (authorization) dont worry too much about repeating code once
Travis
TravisOP2y ago
I am not doubting you...but I still want to believe there's a way to avoid the near-repetition with validation. 😅 Thx again.
Want results from more Discord servers?
Add your server