diff --git a/docs/en/topics/datamodel.md b/docs/en/topics/datamodel.md
index f11afe2fb..6633690a3 100755
--- a/docs/en/topics/datamodel.md
+++ b/docs/en/topics/datamodel.md
@@ -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
diff --git a/docs/en/topics/form-validation.md b/docs/en/topics/form-validation.md
index ce1abad68..a6a549537 100644
--- a/docs/en/topics/form-validation.md
+++ b/docs/en/topics/form-validation.md
@@ -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
+ TextField::create('MyText')->setAttribute('required', true);
+
+ // Markup contains
+ 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
+ DateField::create('MyDate')->setConfig('dateformat', 'dd.MM.yyyy');
+
+ // Limit extensions on upload (in PHP)
+ // Markup contains
+ $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()]
\ No newline at end of file