Skip to content

Commit

Permalink
DOC Use FieldValidators for FormFieldValidation
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Nov 12, 2024
1 parent ede5aa4 commit c27e818
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 24 deletions.
39 changes: 15 additions & 24 deletions en/02_Developer_Guides/03_Forms/01_Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,62 +77,53 @@ In this example we will be required to input a value for `Name` and a valid emai
## Extensions

Extensions applied to `FormField`, or subclasses, can hook into the validation logic and adjust the results by utilising
the `updateValidationResult` method. For example, an extension applied to `EmailField` could look like this:
the `updateValidate` method. For example, an extension applied to `EmailField` could look like this:

```php
namespace App\Extension;

use SilverStripe\Core\Extension;
use SilverStripe\Forms\Validator;
use SilverStripe\Core\Validation\ValidationResult;

class FormFieldValidationExtension extends Extension
{
protected function updateValidationResult(bool &$result, Validator $validator)
protected function updateValidate(ValidationResult $result): void
{
if (str_ends_with($this->owner->Value(), '@example.com')) {
$validator->validationError($this->owner->Name(), 'Please provide a valid email address');
$result = false;
$result->addFieldError($this->owner->Name(), 'Please provide a valid email address');
}
}
}
```

> [!WARNING]
> This extension hook will not work without the ampersand (`&`) in the `&$result` argument. This is because the return
> value of the function is ignored, so the validation result has to be updated by changing the value of the `$result`
> variable. This is known as [passing by reference](https://www.php.net/manual/en/language.references.pass.php).
## Validation in `FormField` subclasses

Subclasses of `FormField` can define their own version of `validate` to provide custom validation rules such as the
above example with the `Email` validation. The `validate` method on `FormField` takes a single argument of the current
`Validator` instance.
above example with the `Email` validation.

```php
namespace App\Form\Field;

use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Forms\NumericField;

class CustomNumberField extends NumericField
{
// ...

public function validate($validator)
public function validate(): ValidationResult
{
if ((int) $this->Value() === 10) {
$validator->validationError($this->Name(), 'This value cannot be 10');
return $this->extendValidationResult(false, $validator);
}

return $this->extendValidationResult(true, $validator);
$result = ValidationResult::create();
$this->beforeExtending('updateValidate', function () use ($result) {
if ((int) $this->Value() === 10) {
$result->addFieldError($this->Name(), 'This value cannot be 10');
}
});
return $result->combineAnd(parent::validate());
}
}
```

The `validate` method should compute a boolean (`true` if the value passes validation and `false` if Silverstripe CMS
should trigger a validation error on the page) and pass this to the `extendValidationResult` method to allow extensions
to hook into the validation logic. In addition, in the event of failed validation, a useful error message must be set
on the given validator.
The `validate` method should create a [`ValidationResult`](api:SilverStripe\Core\Validation\ValidationResult) object and add any errors to it, which should contain a useful error message. The `validate` method should utilise the `$this->beforeExtending('updateValidate', ...` block and be followed by `return $result->combineAnd(parent::validate());` in order for code in the parent class to be executed in the correct order.

> [!WARNING]
> You can also override the entire `Form` validation by subclassing `Form` and defining a `validate` method on the form.
Expand Down
8 changes: 8 additions & 0 deletions en/08_Changelogs/6.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,14 @@ PHP code such as resolvers that were in [`silverstripe/asset-admin`](http://gith

If your project does not have any custom GraphQL, after upgrading you may still have the old `.graphql-generated` and `public/_graphql` folders in your project. You can safely remove these folders.

### `FormField` classes now use `FieldValidator` for validation

Many of [`FormField`](api:SilverStripe\Forms\FormField) subclasses in the `SilverStripe\Forms` namespace now use `FieldValidator` classes for validation, which are also used for `DBField` validation. This has meant that much of the old `validate()` logic on `FormField` subclasses has been removed as it was duplicated in the `FieldValidator` classes. Some custom in `validate()` methods not found in `FieldValidator` classes methods has been retained.

As part of this change, the [`FormField::validate()`](api:SilverStripe\Forms\FormField::validate()) now returns a `ValidationResult` object rather than a `bool` which it previously did. Also, the `$validator` parameter has been removed. If you have implemented a custom `validate()` method in a `FormField` subclass, you will need to update it to return a `ValidationResult` object instead and remove the `$validator` parameter.

Also, the `extendValidationResult()` method and the `updateValidationResult` extension hook on `FormField` have both been removed and replaced with an `updateValidate` hook instead which has a single `ValidationResult $result` parameter.

### Most extension hook methods are now protected {#hooks-protected}

Core implementations of most extension hooks such as `updateCMSFields()` now have protected visibility. Formerly they had public visibility which meant they could be called directly which was not how they were intended to be used. Extension hook implementations are still able to be declared public in project code, though it is recommended that all extension hook methods are declared protected in project code to follow best practice.
Expand Down

0 comments on commit c27e818

Please sign in to comment.