silverstripe-framework/docs/en/02_Developer_Guides/03_Forms/01_Validation.md

236 lines
6.8 KiB
Markdown
Raw Normal View History

title: Form Validation
summary: Validate form data through the server side validation API.
# Form Validation
SilverStripe provides server-side form validation out of the box through the [api:Validator] class and its' child class
[api:RequiredFields]. A single `Validator` instance is set on each `Form`. Validators are implemented as an argument to
the `[api:Form]` constructor or through the function `setValidator`.
:::php
<?php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'MyForm'
);
public function MyForm() {
$fields = new FieldList(
TextField::create('Name'),
EmailField::create('Email')
);
$actions = new FieldList(
FormAction::create('doSubmitForm', 'Submit')
);
// the fields 'Name' and 'Email' are required.
$required = new RequiredFields(array(
'Name', 'Email'
));
// $required can be set as an argument
$form = new Form($controller, 'MyForm', $fields, $actions, $required);
// Or, through a setter.
$form->setValidator($required);
return $form;
}
public function doSubmitForm($data, $form) {
//..
}
}
In this example we will be required to input a value for `Name` and a valid email address for `Email` before the
`doSubmitForm` method is called.
<div class="info" markdown="1">
Each individual [api:FormField] instance is responsible for validating the submitted content through the
[api:FormField::validate] method. By default, this just checks the value exists. Fields like `EmailField` override
`validate` to check for a specific format.
</div>
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.
<div class="notice" markdown="1">
The data value of the `FormField` submitted is not passed into validate. It is stored in the `value` property through
the `setValue` method.
</div>
:::php
public function validate($validator) {
if($this->value == 10) {
return false;
}
return true;
}
The `validate` method should return `true` if the value passes any validation and `false` if SilverStripe should trigger
a validation error on the page.
<div class="notice" markdown="1">
You can also override the entire `Form` validation by subclassing `Form` and defining a `validate` method on the form.
</div>
Say we need a custom `FormField` which requires the user input a value in a `TextField` between 2 and 5. There would be
two ways to go about this:
A custom `FormField` which handles the validation. This means the `FormField` can be reused throughout the site and have
the same validation logic applied to it throughout.
**mysite/code/formfields/CustomNumberField.php**
:::php
<?php
class CustomNumberField extends TextField {
public function validate($validator) {
if(!is_numeric($this->value)) {
$validator->validationError(
$this->name, "Not a number. This must be between 2 and 5", "validation", false
);
return false;
}
else if($this->value > 5 || $this->value < 2) {
$validator->validationError(
2015-02-03 11:11:51 +01:00
$this->name, "Your number must be between 2 and 5", "validation", false
);
return false;
}
return true;
}
}
Or, an alternative approach to the custom class is to define the behavior inside the Form's action method. This is less
reusable and would not be possible within the `CMS` or other automated `UI` but does not rely on creating custom
`FormField` classes.
:::php
<?php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'MyForm'
);
public function MyForm() {
$fields = new FieldList(
TextField::create('Name'),
EmailField::create('Email')
);
$actions = new FieldList(
FormAction::create('doSubmitForm', 'Submit')
);
$form = new Form($controller, 'MyForm', $fields, $actions);
return $form;
}
public function doSubmitForm($data, $form) {
// At this point, RequiredFields->validate() will have been called already,
// so we can assume that the values exist. Say we want to make sure that email hasn't already been used.
$check = Member::get()->filter('Email', $data['Email'])->first();
if($check) {
$form->addErrorMessage('Email', 'This email already exists', 'bad');
return $this->redirectBack();
}
$form->sessionMessage("You have been added to our mailing list", 'good');
return $this->redirectBack();
}
}
## Server-side validation messages
If a `FormField` fails to pass `validate()` the default error message is returned.
:::php
'$Name' is required
Use `setCustomValidationMessage` to provide a custom message.
:::php
$field = new TextField(..);
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
## JavaScript validation
Although there are no built-in JavaScript validation handlers in SilverStripe, the `FormField` API is flexible enough
to provide the information required in order to plug in custom libraries like [Parsley.js](http://parsleyjs.org/) or
[jQuery.Validate](http://jqueryvalidation.org/). Most of these libraries work on HTML `data-` attributes or special
classes added to each input. For Parsley we can structure the form like.
:::php
$form = new Form(..);
$form->setAttribute('data-parsley-validate', true);
$field = $fields->dataFieldByName('Name');
$field->setAttribute('required', true);
$field->setAttribute('data-parsley-mincheck', '2');
## Model Validation
An alternative (or additional) approach to validation is to place it directly on the database model. SilverStripe
provides a `[api:DataObject->validate]` method to validate data at the model level. See
[Data Model Validation](../model/validation).
### Validation in the CMS
In the CMS, we're not creating the forms for editing CMS records. The `Form` instance is generated for us so we cannot
call `setValidator` easily. However, a `DataObject` can provide its' own `Validator` instance through the
`getCMSValidator()` method. The CMS interfaces such as [api:LeftAndMain], [api:ModelAdmin] and [api:GridField] will
respect the provided `Validator` and handle displaying error and success responses to the user.
<div class="info" markdown="1">
Again, custom error messages can be provided through the `FormField`
</div>
:::php
<?php
class Page extends SiteTree {
private static $db = array(
'MyRequiredField' => 'Text'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main',
TextField::create('MyRequiredField')->setCustomValidationMessage('You missed me.')
);
}
public function getCMSValidator() {
return new RequiredFields(array(
'MyRequiredField'
));
}
## API Documentation
* [api:RequiredFields]
2015-02-03 11:11:51 +01:00
* [api:Validator]