Skip to content

Commit

Permalink
Remove validation errors of hidden steps
Browse files Browse the repository at this point in the history
  • Loading branch information
aerni committed May 3, 2024
1 parent de63e57 commit 1be19d8
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 30 deletions.
18 changes: 3 additions & 15 deletions src/Form/Step.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,30 +83,18 @@ public function hasErrors(): bool

public function validate(): bool
{
/**
* The error bag is reset when the validation of the current step passes.
* This leads to error messages of other steps being reset as well.
* To prevent this, we can just return early if the current step has errors.
* The fields of the step are still being validated with validateOnly() in the updatedFields() method.
*/
if ($this->hasErrors()) {
return false;
}

Livewire::current()->storeAllStepErrors();

$rules = $this->fields()
->mapWithKeys(fn (Field $field) => $field->rules())
->toArray();

Livewire::current()->validate($rules);

/*
* The error bag is reset when the current step is validated.
* The error bag is reset when the validation of a step passes.
* This leads to error messages of other steps being reset as well.
* To prevent this, we restore the previous error bag after the validation.
* To prevent this, we restore the previous error bag.
*/
Livewire::current()->restoreAllStepErrors();
Livewire::current()->setStepErrors();

return true;
}
Expand Down
58 changes: 43 additions & 15 deletions src/Livewire/Concerns/HandlesValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,73 @@

namespace Aerni\LivewireForms\Livewire\Concerns;

use Illuminate\Support\MessageBag;
use Livewire\Attributes\Locked;
use Aerni\LivewireForms\Enums\StepStatus;
use Illuminate\Contracts\Validation\Validator;

trait HandlesValidation
{
// TODO: Should this only be added for the WizardForm?
#[Locked]
public array $allStepErrors = [];
public array $stepErrors = [];

public function bootHandlesValidation(): void
{
/**
* Remove all fields that are not submittable from the data before validation to replicate
* Statamic's suggested validation pattern: https://statamic.dev/conditional-fields#validation
* This allows us to conditionally apply validation to conditionally shown fields using the 'sometimes' rule.
*/
$this->withValidator(function ($validator) {

/**
* Remove all fields that are not submittable from the data before validation to replicate
* Statamic's suggested validation pattern: https://statamic.dev/conditional-fields#validation
* This allows us to conditionally apply validation to conditionally shown fields using the 'sometimes' rule.
*/
collect($validator->getValue('fields'))
->filter(fn ($value, $field) => $this->submittableFields[$field])
->pipe(fn ($fields) => $validator->setValue('fields', $fields));

/**
* Validation errors in a WizardForm need special treatment.
*/
if (property_exists($this, 'currentStep')) {
$validator->after(function ($validator) {
/* Store the current errors so that we can restore them later. */
$this->storeStepErrors($validator);

/**
* If the validation of the current step fails, we need to merge all previously stored errors
* to ensure that we don't reset the validation state of other steps in the process.
*/
if ($validator->errors()->hasAny($this->currentStep()->fields()->map->key()->all())) {
/* Ensure we don't add errors that already exist. */
$errors = array_diff_key($this->stepErrors, $validator->errors()->messages());
$validator->errors()->merge($errors);
}
});
}

});
}

public function storeAllStepErrors(): void
protected function storeStepErrors(Validator $validator): void
{
$currentErrors = $this->getErrorBag()->messages();
$currentErrors = $validator->errors()->messages();

$currentStepFields = $this->currentStep()->fields()->map->key()->flip();

$stepFields = $this->currentStep()->fields()->map->key()->flip();
$hiddenStepFields = $this->steps->where('status', StepStatus::Invisible)->flatMap->fields()->map->key()->flip();

$resolvedErrors = collect($stepFields)->diffKeys($currentErrors);
$fieldsWithNoErrors = $currentStepFields->merge($hiddenStepFields)->diffKeys($currentErrors);

$this->allStepErrors = collect($this->allStepErrors)
$this->stepErrors = collect($this->stepErrors)
->merge($currentErrors)
->diffKeys($resolvedErrors)
->diffKeys($fieldsWithNoErrors) /* Ensure we remove resolved errors */
->toArray();

$this->setStepErrors();
}

public function restoreAllStepErrors(): void
public function setStepErrors(): void
{
$this->setErrorBag(new MessageBag($this->allStepErrors));
$this->setErrorBag($this->stepErrors);
}

protected function rules(): array
Expand Down

0 comments on commit 1be19d8

Please sign in to comment.