Validate the form data when using the replicateAction in RelationManager

In my RelationManager Resource I add the replicate action on rows I like to present during this replicate a form with the data so that users can change attributes. Since this is replication existing data it is valid but I have a unique constraint in this case on 2 fields added that should be checked on the replicated data before save and inform the users to change the 1 of those fields. When submitting, the data doesn't seem validated although I have the unique constraint on the RelationManager form. Do I need to do something special to trigger the validation on submit or is there a other way then what I already tried? This it the code to show form on replicateAction and change some data:
Tables\Actions\ReplicateAction::make()
->form(function(Form $form, Set $set) {
$replica = $form->getRecord();
$replica->fill([
... setting some metadata fields like 'created_by', 'created_at', 'changed_by''updated_at'
]);
return static::form($form)->columns(2);
})
Tables\Actions\ReplicateAction::make()
->form(function(Form $form, Set $set) {
$replica = $form->getRecord();
$replica->fill([
... setting some metadata fields like 'created_by', 'created_at', 'changed_by''updated_at'
]);
return static::form($form)->columns(2);
})
I tried to add the unique rules again without positive result
->beforeFormValidated(function (Model $record, Get $get) {
$validator = Validator::make($record->getAttributes(),
['specification' => [
Rule::unique('table')->where(function ($query) use ($record) {
return $query
->where('field1', strtoupper($record->field1))
->where('field2', $record->field2);
})
]
]
);
})
->beforeFormValidated(function (Model $record, Get $get) {
$validator = Validator::make($record->getAttributes(),
['specification' => [
Rule::unique('table')->where(function ($query) use ($record) {
return $query
->where('field1', strtoupper($record->field1))
->where('field2', $record->field2);
})
]
]
);
})
Solution:
Just to give the solution I think is the best ```php ->unique( // workaround for ignore record on replication...
Jump to solution
8 Replies
Zamion101
Zamion1012w ago
When I looked at the source code of the Filament for beforeFormValidated and how it's used, I see that it been called directly and in the function where it's called it catched ValidationException so I think you need to trigger or throw ValidationException
Zamion101
Zamion1012w ago
GitHub
filament/packages/actions/src/Concerns/InteractsWithActions.php at ...
A powerful open source UI framework for Laravel • Build and ship admin panels & apps fast with Livewire - filamentphp/filament
BjornVB
BjornVBOP2w ago
I'm not sure what you refer to, do you means I need to grab the validation exceptions myself? How would I link it to the specific field in the ui?
Zamion101
Zamion1012w ago
What I am saying is that you need to get the Validator exception and throw it using throw $validationException; or throw new ValidationException(...). Filament does not know anything about the $validator in your function and nor care, it looks for ValidationException to handle Validation errors
BjornVB
BjornVBOP2w ago
I remove the beforeFormValidated and added the following code
->beforeReplicaSaved(function ($action, $replica, $livewire): void {
$uniqueCount = Model::query()->where(['field1' => $replica->field1])
->where('field2', $replica->field2)->count();

throw_if($uniqueCount > 0, ValidationException::withMessages([
'mountedTableActionsData.0.field1' => 'The Field1 is already registered.',
]));
}),
->beforeReplicaSaved(function ($action, $replica, $livewire): void {
$uniqueCount = Model::query()->where(['field1' => $replica->field1])
->where('field2', $replica->field2)->count();

throw_if($uniqueCount > 0, ValidationException::withMessages([
'mountedTableActionsData.0.field1' => 'The Field1 is already registered.',
]));
}),
Needed to experiment with the validationException using 'data.field1' and 'mountedActionData.field1' had no effect but the replicate was stopped Needed to use 'mountedTableActionsData.0.field1' and not sure why I needed to use the extra 0, have tested thiw with more records in case this was using a order of the RelationManager table show but it's not the case. I have a solution now but still like to know why I needed to use the .0. to pinpoint the ui field? I did some extra test, I still think it needs to use the rules defined on the form. I do get the rules mentioned for mountedTableActionForm but getValidationMessages are empty in afterFormValidated Seems validation is not called on mountedTableActionForm
Zamion101
Zamion1012w ago
You can open an issue with reproduction repo on github to be investigated. Please explain in details though.
BjornVB
BjornVBOP2w ago
Before I open an issue I want to be more sure I'am not doing anything not logical (not to say something stupid) Did a test with a required rule, removed the value of a field and on submit the rule rule is triggered without me needing to do anything extra like adding the beforeFormValidated or beforeReplicaSaved and checked also if the rule is still mentioned in beforeFormValidated. It seems the unique rule is not triggering specific. Did I something strange in setting this unique rule on 2 fields?
->unique(
ignoreRecord: true,
modifyRuleUsing: function (Unique $rule, Get $get) {
return $rule
->where('field1', strtoupper($get('field1')))
->where('field2', $get('field2'));
}
)
->unique(
ignoreRecord: true,
modifyRuleUsing: function (Unique $rule, Get $get) {
return $rule
->where('field1', strtoupper($get('field1')))
->where('field2', $get('field2'));
}
)
edited: removing the ignoreRecord seems to solve it, I do need this on edit but not on replicate which is in theory also a create with already defined fields
Solution
BjornVB
BjornVB6d ago
Just to give the solution I think is the best
->unique(
// workaround for ignore record on replication
modifyRuleUsing: function (Unique $rule, Get $get, $operation, $record) {
return $rule
->ignore(in_array($operation, ['replicate']) ? null : $record->id ?? null)
->where('field1', strtoupper($get('field1')))
->where('field2', $get('field2'));
}
)
->unique(
// workaround for ignore record on replication
modifyRuleUsing: function (Unique $rule, Get $get, $operation, $record) {
return $rule
->ignore(in_array($operation, ['replicate']) ? null : $record->id ?? null)
->where('field1', strtoupper($get('field1')))
->where('field2', $get('field2'));
}
)

Did you find this page helpful?