mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-03 14:48:38 +02:00
Merge pull request #2867 from oddnoc/form-docs-consolidation
Combine form validation topic with forms topic
This commit is contained in:
commit
ae31362e21
@ -720,7 +720,7 @@ an object, not for displaying the objects contained in the relation.
|
|||||||
## Validation and Constraints
|
## Validation and Constraints
|
||||||
|
|
||||||
Traditionally, validation in SilverStripe has been mostly handled on the
|
Traditionally, validation in SilverStripe has been mostly handled on the
|
||||||
controller through [form validation](/topics/form-validation).
|
controller through [form validation](/topics/forms#form-validation).
|
||||||
|
|
||||||
While this is a useful approach, it can lead to data inconsistencies if the
|
While this is a useful approach, it can lead to data inconsistencies if the
|
||||||
record is modified outside of the controller and form context.
|
record is modified outside of the controller and form context.
|
||||||
|
@ -1,159 +0,0 @@
|
|||||||
# Form Validation
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
## 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() {
|
|
||||||
$form = new Form($this, 'Form',
|
|
||||||
new FieldList(
|
|
||||||
new TextField('MyRequiredField'),
|
|
||||||
new TextField('MyOptionalField')
|
|
||||||
),
|
|
||||||
new FieldList(
|
|
||||||
new FormAction('submit', 'Submit form')
|
|
||||||
),
|
|
||||||
new RequiredFields(array('MyRequiredField'))
|
|
||||||
);
|
|
||||||
// Optional: Add a CSS class for custom styling
|
|
||||||
$form->dataFieldByName('MyRequiredField')->addExtraClass('required');
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
## 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 {
|
|
||||||
private static $allowed_actions = array('Form');
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Validation in the CMS
|
|
||||||
|
|
||||||
Since you're not creating the forms for editing CMS records,
|
|
||||||
SilverStripe provides you with a `getCMSValidator()` method on your models
|
|
||||||
to return a `[api:Validator]` instance.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class Page extends SiteTree {
|
|
||||||
private static $db = array('MyRequiredField' => 'Text');
|
|
||||||
|
|
||||||
public function getCMSValidator() {
|
|
||||||
return new RequiredFields(array('MyRequiredField'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## 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
|
|
||||||
errors.
|
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
* Model Validation with [api:DataObject->validate()]
|
|
@ -1,6 +1,6 @@
|
|||||||
# Forms
|
# Forms
|
||||||
|
|
||||||
HTML forms are in practice the most used way to communicate with a browser.
|
HTML forms are in practice the most used way to interact with a user.
|
||||||
SilverStripe provides classes to generate and handle the actions and data from a
|
SilverStripe provides classes to generate and handle the actions and data from a
|
||||||
form.
|
form.
|
||||||
|
|
||||||
@ -9,14 +9,14 @@ form.
|
|||||||
A fully implemented form in SilverStripe includes a couple of classes that
|
A fully implemented form in SilverStripe includes a couple of classes that
|
||||||
individually have separate concerns.
|
individually have separate concerns.
|
||||||
|
|
||||||
* Controller - Takes care of assembling the form and receiving data from it.
|
* Controller—Takes care of assembling the form and receiving data from it.
|
||||||
* Form - Holds sets of fields, actions and validators.
|
* Form—Holds sets of fields, actions and validators.
|
||||||
* FormField - Fields that receive data or displays them, e.g input fields.
|
* FormField —Fields that receive data or displays them, e.g input fields.
|
||||||
* FormActions - Often submit buttons that executes actions.
|
* FormActions—Often submit buttons that executes actions.
|
||||||
* Validators - Validate the whole form, see [Form validation](form-validation.md) topic for more information.
|
* Validators—Validate the whole form.
|
||||||
|
|
||||||
Depending on your needs you can customize and override any of the above classes,
|
Depending on your needs you can customize and override any of the above classes;
|
||||||
however the defaults are often sufficient.
|
the defaults, however, are often sufficient.
|
||||||
|
|
||||||
## The Controller
|
## The Controller
|
||||||
|
|
||||||
@ -53,12 +53,13 @@ in a controller.
|
|||||||
The name of the form ("HelloForm") is passed into the `Form` constructor as a
|
The name of the form ("HelloForm") is passed into the `Form` constructor as a
|
||||||
second argument. It needs to match the method name.
|
second argument. It needs to match the method name.
|
||||||
|
|
||||||
Since forms need a URL, the `HelloForm()` method needs to be handled like any
|
Because forms need a URL, the `HelloForm()` method needs to be handled like any
|
||||||
other controller action. In order to whitelist its access through URLs, we add
|
other controller action. To grant it access through URLs, we add it to the
|
||||||
it to the `$allowed_actions` array.
|
`$allowed_actions` array.
|
||||||
|
|
||||||
Form actions ("doSayHello") on the other hand should NOT be included here, these
|
Form actions ("doSayHello"), on the other hand, should _not_ be included in
|
||||||
are handled separately through `Form->httpSubmission()`.
|
`$allowed_actions`; these are handled separately through
|
||||||
|
`Form->httpSubmission()`.
|
||||||
|
|
||||||
You can control access on form actions either by conditionally removing a
|
You can control access on form actions either by conditionally removing a
|
||||||
`FormAction` from the form construction, or by defining `$allowed_actions` in
|
`FormAction` from the form construction, or by defining `$allowed_actions` in
|
||||||
@ -68,19 +69,21 @@ your own `Form` class (more information in the
|
|||||||
**Page.ss**
|
**Page.ss**
|
||||||
|
|
||||||
:::ss
|
:::ss
|
||||||
<!-- place where you would like the form to show up -->
|
<%-- place where you would like the form to show up --%>
|
||||||
<div>$HelloForm</div>
|
<div>$HelloForm</div>
|
||||||
|
|
||||||
<div class="warning" markdown='1'>
|
<div class="warning" markdown='1'>
|
||||||
Be sure to add the Form name 'HelloForm' to the Controller::$allowed_actions()
|
Be sure to add the Form name 'HelloForm' to your controller's $allowed_actions
|
||||||
to be sure that form submissions get through to the correct action.
|
array to enable form submissions.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
You'll notice that we've used a new notation for creating form fields, using `create()` instead of the `new` operator.
|
You'll notice that we've used a new notation for creating form fields, using
|
||||||
These are functionally equivalent, but allows PHP to chain operations like `setTitle()` without assigning the field
|
`create()` instead of the `new` operator. These are functionally equivalent, but
|
||||||
instance to a temporary variable. For in-depth information on the create syntax, see the [Injector](/reference/injector)
|
allows PHP to chain operations like `setTitle()` without assigning the field
|
||||||
documentation or the API documentation for `[api:Object]`::create().
|
instance to a temporary variable. For in-depth information on the create syntax,
|
||||||
|
see the [Injector](/reference/injector) documentation or the API documentation
|
||||||
|
for `[api:Object]`::create().
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## The Form
|
## The Form
|
||||||
@ -95,13 +98,18 @@ Creating a form is a matter of defining a method to represent that form. This
|
|||||||
method should return a form object. The constructor takes the following
|
method should return a form object. The constructor takes the following
|
||||||
arguments:
|
arguments:
|
||||||
|
|
||||||
* `$controller`: This must be an instance of the controller that contains the form, often `$this`.
|
* `$controller`: This must be an instance of the controller that contains the
|
||||||
* `$name`: This must be the name of the method on that controller that is called to return the form. The first two
|
form, often `$this`.
|
||||||
fields allow the form object to be re-created after submission. **It's vital that they are properly set - if you ever
|
* `$name`: This must be the name of the method on that controller that is
|
||||||
have problems with form action handler not working, check that these values are correct.**
|
called to return the form. The first two arguments allow the form object
|
||||||
* `$fields`: A `[api:FieldList]` containing `[api:FormField]` instances make up fields in the form.
|
to be re-created after submission. **It's vital that they be properly
|
||||||
* `$actions`: A `[api:FieldList]` containing the `[api:FormAction]` objects - the buttons at the bottom.
|
set—if you ever have problems with a form action handler not working,
|
||||||
* `$validator`: An optional `[api:Validator]` for validation of the form.
|
check that these values are correct.**
|
||||||
|
* `$fields`: A `[api:FieldList]` containing `[api:FormField]` instances make
|
||||||
|
up fields in the form.
|
||||||
|
* `$actions`: A `[api:FieldList]` containing the `[api:FormAction]` objects -
|
||||||
|
the buttons at the bottom.
|
||||||
|
* `$validator`: An optional `[api:Validator]` for validation of the form.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -119,13 +127,14 @@ Example:
|
|||||||
|
|
||||||
## Subclassing a form
|
## Subclassing a form
|
||||||
|
|
||||||
It's the responsibility of your subclass' constructor to call
|
It's the responsibility of your subclass's constructor to call
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
parent::__construct()
|
parent::__construct()
|
||||||
|
|
||||||
with the right parameters. You may choose to take $fields and $actions as arguments if you wish, but $controller and
|
with the right parameters. You may choose to take $fields and $actions as
|
||||||
$name must be passed - their values depend on where the form is instantiated.
|
arguments if you wish, but $controller and $name must be passed—their values
|
||||||
|
depend on where the form is instantiated.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyForm extends Form {
|
class MyForm extends Form {
|
||||||
@ -141,8 +150,9 @@ $name must be passed - their values depend on where the form is instantiated.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
The real difference, however, is that you can then define your controller methods within the form class itself. This
|
The real difference, however, is that you can then define your controller
|
||||||
means that the form takes responsibilities from the controller and manage how to parse and use the form
|
methods within the form class itself. This means that the form takes
|
||||||
|
responsibilities from the controller and manage how to parse and use the form
|
||||||
data.
|
data.
|
||||||
|
|
||||||
**Page.php**
|
**Page.php**
|
||||||
@ -211,10 +221,10 @@ form.
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
## Readonly
|
## Readonly
|
||||||
|
|
||||||
You can turn a form or individual fields into a readonly version. This is handy
|
You can turn a form or individual fields into a readonly version. This is handy
|
||||||
in the case of confirmation pages or when certain fields can be edited due to
|
in the case of confirmation pages or when certain fields cannot be edited due to
|
||||||
permissions.
|
permissions.
|
||||||
|
|
||||||
Readonly on a Form
|
Readonly on a Form
|
||||||
@ -241,10 +251,13 @@ Readonly on a FormField
|
|||||||
|
|
||||||
You can use a custom form template to render with, instead of *Form.ss*
|
You can use a custom form template to render with, instead of *Form.ss*
|
||||||
|
|
||||||
It's recommended you only do this if you've got a lot of presentation text, graphics that surround the form fields. This
|
It's recommended you do this only if you have a lot of presentation text or
|
||||||
is better than defining those as *LiteralField* objects, as it doesn't clutter the data layer with presentation junk.
|
graphics that surround the form fields. This is better than defining those as
|
||||||
|
*LiteralField* objects, as it doesn't clutter the data layer with presentation
|
||||||
|
junk.
|
||||||
|
|
||||||
First of all, you need to create your form on it's own class, that way you can define a custom template using a `forTemplate()` method on your Form class.
|
First you need to create your own form class extending Form; that way you can
|
||||||
|
define a custom template using a `forTemplate()` method on your Form class.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyForm extends Form {
|
class MyForm extends Form {
|
||||||
@ -305,14 +318,16 @@ your project. Here is an example of basic customization:
|
|||||||
<% end_if %>
|
<% end_if %>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
`$Fields.dataFieldByName(FirstName)` will return the form control contents of `Field()` for the particular field object,
|
`$Fields.dataFieldByName(FirstName)` will return the form control contents of
|
||||||
in this case `EmailField->Field()` or `PasswordField->Field()` which returns an `<input>` element with specific markup
|
`Field()` for the particular field object, in this case `EmailField->Field()` or
|
||||||
for the type of field. Pass in the name of the field as the first parameter, as done above, to render it into the
|
`PasswordField->Field()` which returns an `<input>` element with specific markup
|
||||||
template.
|
for the type of field. Pass in the name of the field as the first parameter, as
|
||||||
|
done above, to render it into the template.
|
||||||
|
|
||||||
To find more methods, have a look at the `[api:Form]` class and `[api:FieldList]` class as there is a lot of different
|
To find more methods, have a look at the `[api:Form]` class and
|
||||||
methods of customising the form templates. An example is that you could use `<% loop $Fields %>` instead of specifying
|
`[api:FieldList]` class as there is a lot of different methods of customising
|
||||||
each field manually, as we've done above.
|
the form templates. An example is that you could use `<% loop $Fields %>`
|
||||||
|
instead of specifying each field manually, as we've done above.
|
||||||
|
|
||||||
### Custom form field templates
|
### Custom form field templates
|
||||||
|
|
||||||
@ -333,19 +348,20 @@ Each form field is rendered into a form via the
|
|||||||
`<div>` as well as a `<label>` element (if applicable).
|
`<div>` as well as a `<label>` element (if applicable).
|
||||||
|
|
||||||
You can also render each field without these structural elements through the
|
You can also render each field without these structural elements through the
|
||||||
`[FormField->Field()](api:FormField)` method. In order to influence the form
|
`[FormField->Field()](api:FormField)` method. To influence form rendering,
|
||||||
rendering, overloading these two methods is a good start.
|
overriding these two methods is a good start.
|
||||||
|
|
||||||
In addition, most form fields are rendered through SilverStripe templates, e.g.
|
In addition, most form fields are rendered through SilverStripe templates; for
|
||||||
`TextareaField` is rendered via `framework/templates/forms/TextareaField.ss`.
|
example, `TextareaField` is rendered via
|
||||||
|
`framework/templates/forms/TextareaField.ss`.
|
||||||
|
|
||||||
These templates can be overwritten globally by placing a template with the same
|
These templates can be overridden globally by placing a template with the same
|
||||||
name in your `mysite` directory, or set on a form field instance via anyone of
|
name in your `mysite` directory, or set on a form field instance via any of
|
||||||
these methods:
|
these methods:
|
||||||
|
|
||||||
- FormField->setTemplate()
|
- FormField->setTemplate()
|
||||||
- FormField->setFieldHolderTemplate()
|
- FormField->setFieldHolderTemplate()
|
||||||
- FormField->getSmallFieldHolderTemplate()
|
- FormField->getSmallFieldHolderTemplate()
|
||||||
|
|
||||||
<div class="hint" markdown='1'>
|
<div class="hint" markdown='1'>
|
||||||
Caution: Not all FormFields consistently uses templates set by the above methods.
|
Caution: Not all FormFields consistently uses templates set by the above methods.
|
||||||
@ -358,7 +374,7 @@ by adding a hidden *SecurityID* parameter to each form. See
|
|||||||
[secure-development](/topics/security) for details.
|
[secure-development](/topics/security) for details.
|
||||||
|
|
||||||
In addition, you should limit forms to the intended HTTP verb (mostly `GET` or `POST`)
|
In addition, you should limit forms to the intended HTTP verb (mostly `GET` or `POST`)
|
||||||
to further reduce attack surface, by using `[api:Form->setStrictFormMethodCheck()]`.
|
to further reduce attack exposure, by using `[api:Form->setStrictFormMethodCheck()]`.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$myForm->setFormMethod('POST');
|
$myForm->setFormMethod('POST');
|
||||||
@ -391,12 +407,172 @@ Adds a new text field called FavouriteColour next to the Content field in the CM
|
|||||||
:::php
|
:::php
|
||||||
$this->Fields()->addFieldToTab('Root.Content', new TextField('FavouriteColour'), 'Content');
|
$this->Fields()->addFieldToTab('Root.Content', new TextField('FavouriteColour'), 'Content');
|
||||||
|
|
||||||
|
## Form Validation
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
### 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 that comes with SilverStripe is the `[api:RequiredFields]` class,
|
||||||
|
which ensures that fields are filled out when the form is submitted.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
public function Form() {
|
||||||
|
$form = new Form($this, 'Form',
|
||||||
|
new FieldList(
|
||||||
|
new TextField('MyRequiredField'),
|
||||||
|
new TextField('MyOptionalField')
|
||||||
|
),
|
||||||
|
new FieldList(
|
||||||
|
new FormAction('submit', 'Submit form')
|
||||||
|
),
|
||||||
|
new RequiredFields(array('MyRequiredField'))
|
||||||
|
);
|
||||||
|
// Optional: Add a CSS class for custom styling
|
||||||
|
$form->dataFieldByName('MyRequiredField')->addExtraClass('required');
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
### 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 that 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 are two ways to go about this: attach a custom error message to a specific
|
||||||
|
field, or a generic message to the whole form.
|
||||||
|
|
||||||
|
Example: Validate postcodes based on the selected country (on the controller).
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyController extends Controller {
|
||||||
|
private static $allowed_actions = array('Form');
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
### Validation in the CMS
|
||||||
|
|
||||||
|
Since you're not creating the forms for editing CMS records, SilverStripe
|
||||||
|
provides you with a `getCMSValidator()` method on your models to return a
|
||||||
|
`[api:Validator]` instance.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class Page extends SiteTree {
|
||||||
|
private static $db = array('MyRequiredField' => 'Text');
|
||||||
|
|
||||||
|
public function getCMSValidator() {
|
||||||
|
return new RequiredFields(array('MyRequiredField'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### 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 errors.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
* [Form Field Types](/reference/form-field-types)
|
* [Form Field Types](/reference/form-field-types)
|
||||||
* [MultiForm Module](http://silverstripe.org/multi-form-module)
|
* [MultiForm Module](http://silverstripe.org/multi-form-module)
|
||||||
|
* Model Validation with [api:DataObject->validate()]
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
|
||||||
* `[api:Form]`
|
* `[api:Form]`
|
||||||
* `[api:FormField]`
|
* `[api:FormField]`
|
||||||
|
@ -16,8 +16,7 @@ It is where most documentation should live, and is the natural "second step" aft
|
|||||||
* [Environment management](environment-management): Sharing configuration details (e.g. database login, passwords) with multiple websites via a `_ss_environment.php` file
|
* [Environment management](environment-management): Sharing configuration details (e.g. database login, passwords) with multiple websites via a `_ss_environment.php` file
|
||||||
* [Error Handling](error-handling): Error messages and filesystem logs
|
* [Error Handling](error-handling): Error messages and filesystem logs
|
||||||
* [Files and Images](files): File and Image management in the database and how to manipulate images
|
* [Files and Images](files): File and Image management in the database and how to manipulate images
|
||||||
* [Form Validation](form-validation): Built-in validation on form fields, and how to extend it
|
* [Forms & form validation](forms): Create your own form, add fields and create your own form template using the existing `Form` class
|
||||||
* [Forms](forms): Create your own form, add fields and create your own form template using the existing `Form` class
|
|
||||||
* [Internationalization (i18n)](i18n): Displaying templates and PHP code in different languages using i18n
|
* [Internationalization (i18n)](i18n): Displaying templates and PHP code in different languages using i18n
|
||||||
* [Javascript](javascript): Best practices for developing with JavaScript in SilverStripe
|
* [Javascript](javascript): Best practices for developing with JavaScript in SilverStripe
|
||||||
* [Module Development](module-development): Creating a module (also known as "extension" or "plugin") to contain reusable functionality
|
* [Module Development](module-development): Creating a module (also known as "extension" or "plugin") to contain reusable functionality
|
||||||
|
Loading…
Reference in New Issue
Block a user