Browse files

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

  • Loading branch information...
chillu committed Jun 28, 2012
1 parent 3ef394c commit 0236a3c03a9bf3332a7fc3a8bdb54347de1db0e9
Showing with 141 additions and 36 deletions.
  1. +39 −0 docs/en/topics/
  2. +102 −36 docs/en/topics/
@@ -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
@@ -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
+## 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.
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,49 +29,114 @@ class.
return $form;
-### Subclassing Validator
+## Form Field Validation
-To create your own validator, you need to subclass validator and define two methods:
+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).
- * **javascript()** Should output a snippet of JavaScript that will get called to perform javascript validation.
- * **php($data)** Should return true if the given data is valid, and call $this->validationError() if there were any
+## Adding your own validation messages
-## JavaScript
+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.
-### Default validator.js implementation
+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.
-TODO Describe behaviour.js solution easily, how to disable it
+Example: Validate postcodes based on the selected country (on the controller).
-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.",
+ :::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...
+ }
- });
-### Other validation libraries
+## JavaScript Validation
-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](
+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.
-Disable for all forms (in `mysite/_config.php`):
+### HTML5 attributes
+HTML5 specifies some built-in form validations ([source](,
+which are evaluated by modern browsers without any need for JavaScript.
+SilverStripe supports this by allowing to set custom attributes on fields.
- Validator::set_javascript_validation_handler('none');
+ // 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?://.+')
-Disable for a specific form:
+### 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.
- $myForm->getValidator()->setJavascriptValidationHandler('none');
+ // 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:
+ * **javascript()** Should output a snippet of JavaScript that will get called to perform javascript validation.
+ * **php($data)** Should return true if the given data is valid, and call $this->validationError() if there were any
## Related

0 comments on commit 0236a3c

Please sign in to comment.