NEW Docs for form validation (incl. HTML5 types) and model validatoin

This commit is contained in:
Ingo Schommer 2012-06-28 11:43:30 +02:00
parent 3ef394c448
commit 0236a3c03a
2 changed files with 151 additions and 46 deletions

View File

@ -569,6 +569,45 @@ the described relations).
}
}
## Validation and Constraints
Traditionally, validation in SilverStripe has been mostly handled on the controller
through [form validation](/topics/form-validation).
While this is a useful approach, it can lead to data inconsistencies if the
record is modified outside of the controller and form context.
Most validation constraints are actually data constraints which belong on the model.
SilverStripe provides the `[api:DataObject->validate()]` method for this purpose.
By default, there is no validation - objects are always valid!
However, you can overload this method in your
DataObject sub-classes to specify custom validation,
or use the hook through `[api:DataExtension]`.
Invalid objects won't be able to be written - a [api:ValidationException]`
will be thrown and no write will occur.
It is expected that you call validate() in your own application to test that an object
is valid before attempting a write, and respond appropriately if it isn't.
The return value of `validate()` is a `[api:ValidationResult]` object.
You can append your own errors in there.
Example: Validate postcodes based on the selected country
:::php
class MyObject extends DataObject {
static $db = array(
'Country' => 'Varchar',
'Postcode' => 'Varchar'
);
public function validate() {
$result = parent::validate();
if($this->Country == 'DE' && $this->Postcode && strlen($this->Postcode) != 5) {
$result->error('Need five digits for German postcodes');
}
return $result;
}
}
## Maps
A map is an array where the array indexes contain data as well as the values. You can build a map

View File

@ -1,15 +1,16 @@
# Form Validation
Form validation is a combination of PHP and JavaScript
SilverStripe provides PHP form validation out of the box,
but doesn't come with any built-in JavaScript validation
(the previously used `Validator.js` approach has been deprecated).
## PHP
### Introduction
Validators are implemented as an argument to the `[api:Form]` constructor. You create a required fields validator like
so. In this case, we're creating a `[api:RequiredFields]` validator - the `[api:Validator]` class itself is an abstract
class.
## Required Fields
Validators are implemented as an argument to the `[api:Form]` constructor,
and are subclasses of the abstract `[api:Validator]` base class.
The only implementation which comes with SilverStripe is
the `[api:RequiredFields]` class, which ensures fields are filled out
when the form is submitted.
:::php
public function Form() {
@ -19,7 +20,7 @@ class.
new TextField('MyOptionalField')
),
new FieldList(
new FormAction('submit', 'Submit')
new FormAction('submit', 'Submit form')
),
new RequiredFields(array('MyRequiredField'))
);
@ -28,7 +29,108 @@ class.
return $form;
}
### Subclassing Validator
## Form Field Validation
Form fields are responsible for validating the data they process,
through the `[api:FormField->validate()] method. There are many fields
for different purposes (see ["form field types"](/reference/form-field-types) for a full list).
## Adding your own validation messages
In many cases, you want to add PHP validation which is more complex than
validating the format or existence of a single form field input.
For example, you might want to have dependent validation on
a postcode which depends on the country you've selected in a different field.
There's two ways to go about this: Either you can attach a custom error message
to a specific field, or a generic message for the whole form.
Example: Validate postcodes based on the selected country (on the controller).
:::php
class MyController extends Controller {
public function Form() {
return Form::create($this, 'Form',
new FieldList(
new NumericField('Postcode'),
new CountryDropdownField('Country')
),
new FieldList(
new FormAction('submit', 'Submit form')
),
new RequiredFields(array('Country'))
);
}
public function submit($data, $form) {
// At this point, RequiredFields->validate() will have been called already,
// so we can assume that the values exist.
// German postcodes need to be five digits
if($data['Country'] == 'de' && isset($data['Postcode']) && strlen($data['Postcode']) != 5) {
$form->addErrorMessage('Postcode', 'Need five digits for German postcodes', 'bad');
return $this->redirectBack();
}
// Global validation error (not specific to form field)
if($data['Country'] == 'IR' && isset($data['Postcode']) && $data['Postcode']) {
$form->sessionMessage("Ireland doesn't have postcodes!", 'bad');
return $this->redirectBack();
}
// continue normal processing...
}
}
## JavaScript Validation
While 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.
### HTML5 attributes
HTML5 specifies some built-in form validations ([source](http://www.w3.org/wiki/HTML5_form_additions)),
which are evaluated by modern browsers without any need for JavaScript.
SilverStripe supports this by allowing to set custom attributes on fields.
:::php
// Markup contains <input type="text" required />
TextField::create('MyText')->setAttribute('required', true);
// Markup contains <input type="url" pattern="https?://.+" />
TextField::create('MyText')
->setAttribute('type', 'url')
->setAttribute('pattern', 'https?://.+')
### HTML5 metadata
In addition, HTML5 elements can contain custom data attributes with the `data-` prefix.
These are general purpose attributes, but can be used to hook in your own validation.
:::php
// Validate a specific date format (in PHP)
// Markup contains <input type="text" data-dateformat="dd.MM.yyyy" />
DateField::create('MyDate')->setConfig('dateformat', 'dd.MM.yyyy');
// Limit extensions on upload (in PHP)
// Markup contains <input type="file" data-allowed-extensions="jpg,jpeg,gif" />
$exts = array('jpg', 'jpeg', 'gif');
$validator = new Upload_Validator();
$validator->setAllowedExtensions($exts);
$upload = Upload::create()->setValidator($validator);
$fileField = FileField::create('MyFile')->setUpload(new);
$fileField->setAttribute('data-allowed-extensions', implode(',', $exts));
Note that these examples don't have any effect on the client as such,
but are just a starting point for custom validation with JavaScript.
## Model Validation
An alternative (or additional) approach to validation is to place it directly
on the model. SilverStripe provides a `[api:DataObject->validate()]` method for this purpose.
Refer to the ["datamodel" topic](/topics/datamodel#validation-and-constraints) for more information.
## Subclassing Validator
To create your own validator, you need to subclass validator and define two methods:
@ -36,42 +138,6 @@ To create your own validator, you need to subclass validator and define two meth
* **php($data)** Should return true if the given data is valid, and call $this->validationError() if there were any
errors.
## JavaScript
### Default validator.js implementation
TODO Describe behaviour.js solution easily, how to disable it
Setting fieldEl.requiredErrorMsg or formEl.requiredErrorMsg will override the default error message. Both can include
the string '$FieldLabel', which will be replaced with the field's label. Otherwise, the message is "Please fill out
"$FieldLabel", it is required".
You can use Behaviour to load in the appropriate value:
:::js
Behaviour.register({
'#Form_Form' : {
requiredErrorMsg: "Please complete this question before moving on.",
}
});
### Other validation libraries
By default, SilverStripe forms with an attached Validator instance use the custom Validator.js clientside logic. It is
quite hard to customize, and might not be appropriate for all use-cases. You can disable integrated clientside
validation, and use your own (e.g. [jquery.validate](http://docs.jquery.com/Plugins/Validation)).
Disable for all forms (in `mysite/_config.php`):
:::php
Validator::set_javascript_validation_handler('none');
Disable for a specific form:
:::php
$myForm->getValidator()->setJavascriptValidationHandler('none');
## Related
* Model Validation with [api:DataObject->validate()]