Form Validation#
SilverStripe provides server-side form validation out of the box through the Validator class and its' child class
RequiredFields. A single Validator instance is set on each Form. Validators are implemented as an argument to
the Form constructor or through the function setValidator.
<?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) {
//..
}
}
doSubmitForm method is called.
[info]
Each individual FormField instance is responsible for validating the submitted content through the
FormField::validate() method. By default, this just checks the value exists. Fields like EmailField override
validate to check for a specific format.
[/info]
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.
[notice]
The data value of the FormField submitted is not passed into validate. It is stored in the value property through
the setValue method.
[/notice]
public function validate($validator) {
if($this->value == 10) {
return false;
}
return true;
}
a validation error on the page.
[notice]
You can also override the entire Form validation by subclassing Form and defining a validate method on the form.
[/notice]
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
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(
$this->name, "Your number must be between 2 and 5", "validation", false
);
return false;
}
return true;
}
}
reusable and would not be possible within the CMS or other automated UI but does not rely on creating custom
FormField classes.
<?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.
'$Name' is required
$field = new TextField(..);
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
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 or
jQuery.Validate. Most of these libraries work on HTML data- attributes or special
classes added to each input. For Parsley we can structure the form like.
$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 DataObject::validate() method to validate data at the model level. See Data 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 LeftAndMain, ModelAdmin and GridField will
respect the provided Validator and handle displaying error and success responses to the user.
[info]
Again, custom error messages can be provided through the FormField
[/info]
<?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'
));
}