Rewrite, tidy and format of Forms documentation

This commit is contained in:
Will Rossiter 2014-10-19 21:32:34 +13:00 committed by Cam Findlay
parent 82b1b3d566
commit 9fbbf6d88a
20 changed files with 1604 additions and 1185 deletions

View File

@ -0,0 +1 @@
* stub, talk validate()

View File

@ -1,586 +0,0 @@
# Forms
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
form.
## Overview
A fully implemented form in SilverStripe includes a couple of classes that
individually have separate concerns.
* Controller — Takes care of assembling the form and receiving data from it.
* Form — Holds sets of fields, actions and validators.
* FormField — Fields that receive data or displays them, e.g input fields.
* FormActions — Buttons that execute actions.
* Validators — Validate the whole form.
Depending on your needs you can customize and override any of the above classes;
the defaults, however, are often sufficient.
## The Controller
Forms start at the controller. Here is a simple example on how to set up a form
in a controller.
**Page.php**
:::php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'HelloForm'
);
// Template method
public function HelloForm() {
$fields = new FieldList();
$actions = new FieldList(
FormAction::create("doSayHello")->setTitle("Say hello")
);
$form = new Form($this, 'HelloForm', $fields, $actions);
// Load the form with previously sent data
$form->loadDataFrom($this->request->postVars());
return $form;
}
public function doSayHello($data, Form $form) {
// Do something with $data
return $this->render();
}
}
The name of the form ("HelloForm") is passed into the `Form` constructor as a
second argument. It needs to match the method name.
Because forms need a URL, the `HelloForm()` method needs to be handled like any
other controller action. To grant it access through URLs, we add it to the
`$allowed_actions` array.
Form actions ("doSayHello"), on the other hand, should _not_ be included in
`$allowed_actions`; these are handled separately through
`Form->httpSubmission()`.
You can control access on form actions either by conditionally removing a
`FormAction` from the form construction, or by defining `$allowed_actions` in
your own `Form` class (more information in the
["controllers" topic](/topics/controllers)).
**Page.ss**
:::ss
<%-- place where you would like the form to show up --%>
<div>$HelloForm</div>
<div class="warning" markdown='1'>
Be sure to add the Form name 'HelloForm' to your controller's $allowed_actions
array to enable form submissions.
</div>
<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. These are functionally equivalent, but
allows PHP to chain operations like `setTitle()` without assigning the field
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>
## The Form
Form is the base class of all forms in a SilverStripe application. Forms in your
application can be created either by instantiating the Form class itself, or by
subclassing it.
### Instantiating a form
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
arguments:
* `$controller`: This must be an instance of the controller that contains the
form, often `$this`.
* `$name`: This must be the name of the method on that controller that is
called to return the form. The first two arguments allow the form object
to be re-created after submission. **It's vital that they be properly
set—if you ever have problems with a form action handler not working,
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:
:::php
// Controller action
public function MyCustomForm() {
$fields = new FieldList(
EmailField::create("Email"),
PasswordField::create("Password")
);
$actions = new FieldList(FormAction::create("login")->setTitle("Log in"));
return new Form($this, "MyCustomForm", $fields, $actions);
}
## Subclassing a form
It's the responsibility of your subclass's constructor to call
:::php
parent::__construct()
with the right parameters. You may choose to take $fields and $actions as
arguments if you wish, but $controller and $name must be passed—their values
depend on where the form is instantiated.
:::php
class MyForm extends Form {
public function __construct($controller, $name) {
$fields = new FieldList(
EmailField::create("Email"),
PasswordField::create("Password")
);
$actions = new FieldList(FormAction::create("login")->setTitle("Log in"));
parent::__construct($controller, $name, $fields, $actions);
}
}
The real difference, however, is that you can then define your controller
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.
**Page.php**
:::php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'HelloForm',
);
// Template method
public function HelloForm() {
return new MyForm($this, 'HelloForm');
}
}
**MyForm.php**
:::php
class MyForm extends Form {
public function __construct($controller, $name) {
$fields = new FieldList(
EmailField::create("Email"),
PasswordField::create("Password")
);
$actions = new FieldList(FormAction::create("login")->setTitle("Log in"));
parent::__construct($controller, $name, $fields, $actions);
}
public function login(array $data, Form $form) {
// Authenticate the user and redirect the user somewhere
Controller::curr()->redirectBack();
}
}
## The FormField classes
There are many classes extending `[api:FormField]`. There is a full overview at
[form field types](/reference/form-field-types).
### Using Form Fields
To get these fields automatically rendered into a form element, all you need to
do is create a new instance of the class, and add it to the `FieldList` of the
form.
:::php
$form = new Form(
$this, // controller
"SignupForm", // form name
new FieldList( // fields
TextField::create("FirstName")->setTitle('First name'),
TextField::create("Surname")->setTitle('Last name')->setMaxLength(50),
EmailField::create("Email")->setTitle("Email address")->setAttribute('type', 'email')
),
new FieldList( // actions
FormAction::create("signup")->setTitle("Sign up")
),
new RequiredFields( // validation
"Email", "FirstName"
)
);
## Readonly
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 cannot be edited due to
permissions.
Readonly on a Form
:::php
$myForm->makeReadonly();
Readonly on a FieldList
:::php
$myFieldList->makeReadonly();
Readonly on a FormField
:::php
$myReadonlyField = $myField->transform(new ReadonlyTransformation());
// shortcut
$myReadonlyField = $myField->performReadonlyTransformation();
## Custom form templates
You can use a custom form template to render with, instead of *Form.ss*
It's recommended you do this only if you have a lot of presentation text or
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 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
class MyForm extends Form {
public function __construct($controller, $name) {
$fields = new FieldList(
EmailField::create("Email"),
PasswordField::create("Password")
);
$actions = new FieldList(FormAction::create("login")->setTitle("Log in"));
parent::__construct($controller, $name, $fields, $actions);
}
public function login(array $data, Form $form) {
// Do something with $data
Controller::curr()->redirectBack();
}
public function forTemplate() {
return $this->renderWith(array($this->class, 'Form'));
}
}
`MyForm->forTemplate()` tells the `[api:Form]` class to render with a template
of return value of `$this->class`, which in this case is *MyForm*. If the
template doesn't exist, then it falls back to using Form.ss.
*MyForm.ss* should then be placed into your *templates/Includes* directory for your project. Here is an example of
basic customisation, with two ways of presenting the field and its inline validation:
:::ss
<form $FormAttributes>
<% if $Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
<% end_if %>
<fieldset>
<div id="Email" class="field email">
<label class="left" for="{$FormName}_Email">Email</label>
$Fields.dataFieldByName(Email)
<span id="{$FormName}_error" class="message $Fields.dataFieldByName(Email).MessageType">
$Fields.dataFieldByName(Email).Message
</span>
</div>
<div id="Email" class="field password">
<label class="left" for="{$FormName}_Password">Password</label>
<% with $Fields.dataFieldByName(Password) %>
$field
<% if $Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% end_if %>
<% end_with %>
</div>
$Fields.dataFieldByName(SecurityID)
</fieldset>
<% if $Actions %>
<div class="Actions">
<% loop $Actions %>$Field<% end_loop %>
</div>
<% end_if %>
</form>
`$Fields.dataFieldByName(FirstName)` will return the form control contents of
`Field()` for the particular field object, in this case `EmailField->Field()` or
`PasswordField->Field()` which returns an `<input>` element with specific markup
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 methods of customising
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
The easiest way to customize form fields is adding CSS classes and additional attributes.
:::php
$field = TextField::create('MyText')
->addExtraClass('largeText');
->setAttribute('data-validation-regex', '[\d]*');
Will be rendered as:
:::html
<input type="text" name="MyText" class="text largeText" id="MyForm_MyCustomForm_MyText" data-validation-regex="[\d]*">
Each form field is rendered into a form via the
`[FormField->FieldHolder()](api:FormField)` method, which includes a container
`<div>` as well as a `<label>` element (if applicable).
You can also render each field without these structural elements through the
`[FormField->Field()](api:FormField)` method. To influence form rendering,
overriding these two methods is a good start.
In addition, most form fields are rendered through SilverStripe templates; for
example, `TextareaField` is rendered via
`framework/templates/forms/TextareaField.ss`.
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 any of
these methods:
- FormField->setTemplate()
- FormField->setFieldHolderTemplate()
- FormField->setSmallFieldHolderTemplate()
<div class="hint" markdown='1'>
Caution: Not all FormFields consistently uses templates set by the above methods.
</div>
### Securing forms against Cross-Site Request Forgery (CSRF)
SilverStripe tries to protect users against *Cross-Site Request Forgery (CSRF)*
by adding a hidden *SecurityID* parameter to each form. See
[secure-development](/topics/security) for details.
In addition, you should limit forms to the intended HTTP verb (mostly `GET` or `POST`)
to further reduce attack exposure, by using `[api:Form->setStrictFormMethodCheck()]`.
:::php
$myForm->setFormMethod('POST');
$myForm->setStrictFormMethodCheck(true);
$myForm->setFormMethod('POST', true); // alternative short notation
### Remove existing fields
If you want to remove certain fields from your subclass:
:::php
class MyCustomForm extends MyForm {
public function __construct($controller, $name) {
parent::__construct($controller, $name);
// remove a normal field
$this->Fields()->removeByName('MyFieldName');
// remove a field from a tab
$this->Fields()->removeFieldFromTab('TabName', 'MyFieldName');
}
}
### Working with tabs
Adds a new text field called FavouriteColour next to the Content field in the CMS
:::php
$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');
$fileField = FileField::create('MyFile');
$fileField->getValidator()->setAllowedExtensions($exts);
$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
* [Form Field Types](/reference/form-field-types)
* [MultiForm Module](http://silverstripe.org/multi-form-module)
* Model Validation with [api:DataObject->validate()]
## API Documentation
* `[api:Form]`
* `[api:FormField]`
* `[api:FieldList]`
* `[api:FormAction]`

View File

@ -0,0 +1,311 @@
title: Introduction to Forms
summary: An introduction to creating a Form instance and handling submissions.
# Forms
The HTML `Form` is the most used way to interact with a user. SilverStripe provides classes to generate forms through
the [api:Form] class, [api:FormField] instances to capture data and submissions through [api:FormAction].
<div class="notice" markdown="1">
See the [Forms Tutorial](../../tutorials/forms/) for a step by step process of creating a `Form`
</div>
## Creating a Form
Creating a [api:Form] has the following signature.
:::php
$form = new Form(
$controller, // the Controller to render this form on
$name, // name of the method that returns this form on the controller
FieldList $fields, // list of FormField instances
FieldList $actions, // list of FormAction instances
$required // optional use of RequiredFields object
);
In practice, this looks like:
**mysite/code/Page.php**
:::php
<?php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'HelloForm'
);
public function HelloForm() {
$fields = new FieldList(
TextField::create('Name', 'Your Name')
);
$actions = new FieldList(
FormAction::create("doSayHello")->setTitle("Say hello")
);
$required = new RequiredFields('Name')
$form = new Form($this, 'HelloForm', $fields, $actions, $required);
return $form;
}
public function doSayHello($data, Form $form) {
$form->sessionMessage('Hello '. $data['Name'], 'success');
return $this->redirectBack();
}
}
**mysite/templates/Page.ss**
:::ss
$HelloForm
<div class="info" markdown="1">
The examples above use `FormField::create()` instead of the `new` operator (`new FormField()`). These are functionally
equivalent, but allows PHP to chain operations like `setTitle()` without assigning the field instance to a temporary
variable.
</div>
When constructing the `Form` instance (`new Form($controller, $name)`) both controller and name are required. The
`$controller` and `$name` are used to allow SilverStripe to calculate the origin of the `Form object`. When a user
submits the `HelloForm` from your `contact-us` page the form submission will go to `contact-us/HelloForm` before any of
the [api:FormActions]. The URL is known as the `$controller` instance will know the 'contact-us' link and we provide
`HelloForm` as the `$name` of the form. `$name` **needs** to match the method name.
Because the `HelloForm()` method will be the location the user is taken to, it needs to be handled like any other
controller action. To grant it access through URLs, we add it to the `$allowed_actions` array.
:::php
private static $allowed_actions = array(
'HelloForm'
);
<div class="notice" markdown="1">
Form actions (`doSayHello`), on the other hand, should _not_ be included in `$allowed_actions`; these are handled
separately through [api:Form::httpSubmission].
</div>
## Adding FormFields
Fields in a [api:Form] are represented as a single [api:FieldList] instance containing subclasses of [api:FormField].
Some common examples are [api:TextField] or [api:DropdownField].
:::php
TextField::create($name, $title, $value);
<div class="info" markdown='1'>
A list of the common FormField subclasses is available on the [Common Subclasses](fields/common_subclasses) page.
</div>
The fields are added to the [api:FieldList] `fields` property on the `Form` and can be modified at up to the point the
`Form` is rendered.
:::php
$fields = new FieldList(
TextField::create('Name'),
EmailField::create('Email')
);
$form = new Form($controller, 'MethodName', $fields, ...);
// or use `setFields`
$form->setFields($fields);
// to fetch the current fields..
$fields = $form->getFields();
A field can be appended to the [api:FieldList].
:::php
$fields = $form->Fields();
// add a field
$fields->push(new TextField(..));
// insert a field before another one
$fields->insertBefore(new TextField(..), 'Email');
// insert a field after another one
$fields->insertAfter(new TextField(..), 'Name');
Fields can be fetched after they have been added in.
:::php
$email = $form->Fields()->dataFieldByName('Email');
$email->setTitle('Your Email Address');
Fields can be removed from the form.
:::php
$form->getFields()->removeByName('Email');
<div class="alert" markdown="1">
Forms can be tabbed (such as the CMS interface). In these cases, there are additional functions such as `addFieldToTab`
and `removeFieldByTab` to ensure the fields are on the correct interface. See [Tabbed Forms](tabbed_forms) for more
information on the CMS interface.
</div>
## Modifying FormFields
Each [api:FormField] subclass has a number of methods you can call on it to customize its' behavior or HTML markup. The
default `FormField` object has several methods for doing common operations.
<div class="notice">
Most of the `set` operations will return the object back so methods can be chained.
</div>
:::php
$field = new TextField(..);
$field
->setMaxLength(100)
->setAttribute('placeholder', 'Enter a value..')
->setTitle('');
### Custom Templates
The [api:Form] HTML markup and each of the [api:FormField] instances are rendered into templates. You can provide custom
templates by using the `setTemplate` method on either the `Form` or `FormField`. For more details on providing custom
templates see [Form Templates](form_templates)
:::php
$form = new Form(..);
$form->setTemplate('CustomForm');
// or, for a FormField
$field = new TextField(..);
$field->setTemplate('CustomTextField');
$field->setFieldHolderTemplate('CustomTextField_Holder');
## Adding FormActions
[api:FormAction] objects are displayed at the bottom of the `Form` in the form of a `button` or `input` tag. When a
user presses the button, the form is submitted to the corresponding method.
:::php
FormAction::create($action, $title);
As with [api:FormField], the actions for a `Form` are stored within a [api:FieldList] instance in the `actions` property
on the form.
:::php
public function MyForm() {
$fields = new FieldList(..);
$actions = new FieldList(
FormAction::create('doSubmitForm', 'Submit')
);
$form = new Form($controller, 'MyForm', $fields, $actions);
// Get the actions
$actions = $form->Actions();
// As actions is a FieldList, push, insertBefore, removeByName and other
// methods described for `Fields` also work for actions.
$actions->push(
FormAction::create('doSecondaryFormAction', 'Another Button')
);
$actions->removeByName('doSubmitForm');
$form->setActions($actions);
return $form
}
public function doSubmitForm($data, $form) {
//
}
public function doSecondaryFormAction($data, $form) {
//
}
The first `$action` argument for creating a `FormAction` is the name of the method to invoke when submitting the form
with the particular button. In the previous example, clicking the 'Another Button' would invoke the
`doSecondaryFormAction` method. This action can be defined (in order) on either:
* One of the `FormField` instances.
* The `Form` instance.
* The `Controller` instance.
<div class="notice">
If the `$action` method cannot be found on any of those or is marked as `private` or `protected`, an error will be
thrown.
</div>
The `$action` method takes two arguments:
* `$data` an array containing the values of the form mapped from `$name` => '$value'
* `$form` the submitted [api:Form] instance.
:::php
<?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) {
// Submitted data is available as a map.
echo $data['Name'];
echo $data['Email'];
// You can also fetch the value from the field.
echo $form->Fields()->dataFieldByName('Email')->Value();
// Using the Form instance you can get / set status such as error messages.
$form->sessionMessage("Successful!", 'good');
// After dealing with the data you can redirect the user back.
return $this->redirectBack();
}
}
## Validation
Form validation is handled by the [api:Validator] class and the `validator` property on the `Form` object. The validator
is provided with a name of each of the [api:FormField]s to validate and each `FormField` instance is responsible for
validating its' own data value.
For more information, see the [Form Validation](validation) documentation.
:::php
$validator = new RequiredFields(array(
'Name', 'Email'
));
$form = new Form($this, 'MyForm', $fields, $actions, $validator);
## API Documentation
* [api:Form]
* [api:FormField]
* [api:FieldList]
* [api:FormAction]

View File

@ -0,0 +1,235 @@
title: Form Validation
summary: Validate form data through the server side validation API.
# Form Validation
SilverStripe provides server-side form validation out of the box through the [api:Validator] class and its' child class
[api:RequiredFields]. A single `Validator` instance is set on each `Form`. Validators are implemented as an argument to
the `[api:Form]` constructor or through the function `setValidator`.
:::php
<?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) {
//..
}
}
In this example we will be required to input a value for `Name` and a valid email address for `Email` before the
`doSubmitForm` method is called.
<div class="info" markdown="1">
Each individual [api:FormField] instance is responsible for validating the submitted content through the
[api:FormField::validate] method. By default, this just checks the value exists. Fields like `EmailField` override
`validate` to check for a specific format.
</div>
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.
<div class="notice" markdown="1">
The data value of the `FormField` submitted is not passed into validate. It is stored in the `value` property through
the `setValue` method.
</div>
:::php
public function validate($validator) {
if($this->value == 10) {
return false;
}
return true;
}
The `validate` method should return `true` if the value passes any validation and `false` if SilverStripe should trigger
a validation error on the page.
<div class="notice" markdown="1">
You can also override the entire `Form` validation by subclassing `Form` and defining a `validate` method on the form.
</div>
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
<?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;
}
}
Or, an alternative approach to the custom class is to define the behavior inside the Form's action method. This is less
reusable and would not be possible within the `CMS` or other automated `UI` but does not rely on creating custom
`FormField` classes.
:::php
<?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.
:::php
'$Name' is required
Use `setCustomValidationMessage` to provide a custom message.
:::php
$field = new TextField(..);
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
## 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 like [Parsley.js](http://parsleyjs.org/) or
[jQuery.Validate](http://jqueryvalidation.org/). Most of these libraries work on HTML `data-` attributes or special
classes added to each input. For Parsley we can structure the form like.
:::php
$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 `[api:DataObject->validate]` method to validate data at the model level. See
[Data Model Validation](../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 [api:LeftAndMain], [api:ModelAdmin] and [api:GridField] will
respect the provided `Validator` and handle displaying error and success responses to the user.
<div class="info" markdown="1">
Again, custom error messages can be provided through the `FormField`
</div>
:::php
<?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'
));
}
## API Documentation
* [api:RequiredFields]
* [api:Validator]

View File

@ -1,157 +0,0 @@
# DateField
## Introduction
This `FormField` subclass lets you display an editable date, either in
a single text input field, or in three separate fields for day, month and year.
It also provides a calendar datepicker.
## Adding a DateField
The following example will add a simple DateField to your Page, allowing you to
enter a date manually.
:::php
class Page extends SiteTree {
private static $db = array(
'MyDate' => 'Date',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab(
'Root.Main',
$myDate = new DateField('MyDate', 'Enter a date')
);
return $fields;
}
}
## Custom Dateformat
You can define a custom dateformat for your Datefield based on [Zend_Date constants](http://framework.zend.com/manual/1.12/en/zend.date.constants.html).
:::php
// will display a date in the following format: 31-06-2012
DateField::create('MyDate')->setConfig('dateformat', 'dd-MM-yyyy');
Caution: If you're using natural language date formats like abbreviated month names
alongside the "showcalendar" option, you'll need to ensure the formats
between the calendar widget and the SilverStripe validation are consistent.
As an example for the 'de' locale, check `framework/thirdparty/jquery-ui/datepicker/i18n/jquery.ui.datepicker-de.js`
and compare it to `framework/thirdparty/Zend/Locale/Data/de.xml`
(see `<calendar type="gregorian">` in the XML data).
## Min and Max Dates
Set the minimum and maximum allowed datevalues using the `min` and `max`
configuration settings (in ISO format or strtotime() compatible). Example:
:::php
DateField::create('MyDate')
->setConfig('min', '-7 days')
->setConfig('max', '2012-12-31')
## Separate Day/Month/Year Fields
The following setting will display your DateField as `three input fields` for
day, month and year separately. Any custom dateformat settings will be ignored.
HTML5 placeholders 'day', 'month' and 'year' are enabled by default.
:::php
DateField::create('MyDate')
->setConfig('dmyfields', true)
->setConfig('dmyseparator', '/') // set the separator
->setConfig('dmyplaceholders', 'true'); // enable HTML 5 Placeholders
## Formatting Hints
Its often not immediate apparent which format a field accepts,
and showing the technical format (e.g. `HH:mm:ss`) is of limited
use to the average user. An alternative is to show the current date
in the desired format alongside the field description as an example.
:::php
$dateField = DateField::create('MyDate');
// Show long format as text below the field
$dateField->setDescription(sprintf(
_t('FormField.Example', 'e.g. %s', 'Example format'),
Convert::raw2xml(Zend_Date::now()->toString($dateField->getConfig('dateformat')))
));
// Alternatively, set short format as a placeholder in the field
$dateField->setAttribute('placeholder', $dateField->getConfig('dateformat'));
Note: Fields scaffolded through `[api:DataObject::scaffoldCMSFields()]` automatically
have a description attached to them.
## Calendar Field
The following setting will add a Calendar to a single DateField, using the
`jQuery UI DatePicker widget`
:::php
DateField::create('MyDate')->setConfig('showcalendar', true);
### 'Safe' Dateformats to Use with the Calendar
The jQuery DatePicker doesn't support every constant available for Zend_Date.
If you choose to use the calendar, the following constants should at least be safe:
Constant | xxxxx
-------- | -----
d | numeric day of the month (without leading zero)
dd | numeric day of the month (with leading zero)
EEE | dayname, abbreviated
EEEE | dayname
M | numeric month of the year (without leading zero)
MM | numeric month of the year (with leading zero)
MMM | monthname, abbreviated
MMMM | monthname
y | year (4 digits)
yy | year (2 digits)
yyyy | year (4 digits)
### Calendar localization issues
Unfortunately the day- and monthname values in Zend Date do not always match
those in the existing jQuery UI locale files, so constants like `EEE` or `MMM`,
for day and monthnames could break validation. To fix this we had to slightly
alter the jQuery locale files, situated in
*/framework/thirdparty/jquery-ui/datepicker/i18n/*, to match Zend_Date.
At this moment not all locale files may be present. If a locale file is
missing, the DatePicker calendar will fallback to 'yyyy-MM-dd' whenever day-
and/or monthnames are used. After saving, the correct format will be displayed.
## Contributing jQuery Locale Files
If you find the jQuery locale file for your chosen locale is missing, the
following section will explain how to create one. If you wish to contribute
your file to the SilverStripe core, please check out the guide on
['contributing code'](http://doc.silverstripe.org/framework/en/trunk/misc/contributing/code).
### 1. Get the Sourcefile
You can find a list of locale files for the jQuery UI DatePicker
[in the jQuery source code](https://github.com/jquery/jquery-ui/tree/master/ui/i18n).
### 2. Find your Zend Locale File
The Zend locale files are located in */framework/thirdparty/Zend/Locale/Data/*.
Find the one that has the information for your locale.
### 3. Find the Date Values
You're looking for the `Gregorian` date values for monthnames and daynames in
the Zend locale file. Edit the DatePicker locale File so your *full day- and
monthnames* and *short monthnames* match. For your *short daynames*, use the
first three characters of the full name. Note that Zend dates are `case
sensitive`!
### 4. Filename
Use the original jQuery UI filename 'jquery.ui.datepicker-xx.js', where xx
stands for the locale.

View File

@ -0,0 +1,69 @@
title: Form Templates
summary: Customize the generated HTML for a FormField or an entire Form.
# Form Templates
Most markup generated in SilverStripe can be replaced by custom templates. Both [api:Form] and [api:FormField] instances
can be rendered out using custom templates using `setTemplate`.
:::php
$form = new Form(..);
$form->setTemplate('MyCustomFormTemplate');
// or, just a field
$field = new TextField(..);
$field->setTemplate('MyCustomTextField');
Both `MyCustomTemplate.ss` and `MyCustomTextField.ss` should be located in **mysite/templates/Includes/**
<div class="notice" markdown="1">
It's recommended to copy the contents of the template you're going to replace and use that as a start. For instance, if
you want to create a `MyCustomFormTemplate` copy the contents of `Form.ss` to a `MyCustomFormTemplate.ss` file and
modify as you need.
</div>
By default, Form and Fields follow the SilverStripe Template convention and are rendered into templates of the same
class name (i.e EmailField will attempt to render into `EmailField.ss` and if that isn't found, `TextField.ss` or
finally `FormField.ss`).
<div class="alert" markdown="1">
While you can override all templates using normal view inheritance (i.e defining a `Form.ss`) other modules may rely on
the core template structure. It is recommended to use `setTemplate` and unique templates for specific forms.
</div>
For [api:FormField] instances, there are several other templates that are used on top of the main `setTemplate`.
:::php
$field = new TextField();
$field->setTemplate('CustomTextField');
// Sets the template for the <input> tag. i.e '<input $AttributesHTML />'
$field->setFieldHolderTemplate('CustomTextField_Holder');
// Sets the template for the wrapper around the text field. i.e
// '<div class="text">'
//
// The actual FormField is rendered into the holder via the `$Field`
// variable.
//
// setFieldHolder() is used in most `Form` instances and needs to output
// labels, error messages and the like.
$field->setSmallFieldHolderTemplate('CustomTextField_Holder_Small');
// Sets the template for the wrapper around the text field.
//
// The difference here is the small field holder template is used when the
// field is embedded within another field. For example, if the field is
// part of a `FieldGroup` or `CompositeField` alongside other fields.
All templates are rendered within the scope of the [api:FormField]. To understand more about Scope within Templates as
well as the available syntax, see the [Templates](../templates) documentation.
## Related Documentation
* [How to: Create a lightweight Form](how_tos/lightweight_form)
## API Documentation
* [api:Form]
* [api:FormField]

View File

@ -0,0 +1,76 @@
title: Form Security
summary: Ensure Forms are secure against Cross-Site Request Forgery attacks, bots and other malicious intent.
# Form Security
Whenever you are accepting or asking users to input data to your application there comes an added responsibility that it
should be done as safely as possible. Below outlines the things to consider when building your forms.
## Cross-Site Request Forgery (CSRF)
SilverStripe protect users against [Cross-Site Request Forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
(known as `CSRF`) by adding a SecurityID [api:HiddenField] to each [api:Form] instance. The `SecurityID` contains a
random string generated by [api:SecurityToken] to identify the particular user request vs a third-party forging fake
requests.
<div class="info" markdown="1">
For more information on Cross-Site Request Forgery, consult the [OWASP](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
website.
</div>
The `SecurityToken` automatically added looks something like:
:::php
$form = new Form(..);
echo $form->getSecurityToken()->getValue();
// 'c443076989a7f24cf6b35fe1360be8683a753e2c'
This token value is passed through the rendered Form HTML as a [api:HiddenField].
:::html
<input type="hidden" name="SecurityID" value="c443076989a7f24cf6b35fe1360be8683a753e2c" class="hidden" />
The token should be present whenever a operation has a side effect such as a `POST` operation.
It can be safely disabled for `GET` requests as long as it does not modify the database (i.e a search form does not
normally require a security token).
:::php
$form = new Form(..);
$form->disableSecurityToken();
<div class="alert" markdown="1">
Do not disable the SecurityID for forms that perform some modification to the users session. This will open your
application up to `CSRF` security holes.
</div>
## Strict Form Submission
Forms should be limited to the intended HTTP verb (mostly `GET` or `POST`) to further reduce attack exposure. Without
this check, forms that rely on `GET` can be submitted via `POST` or `PUT` or vice-versa potentially leading to
application errors or edge cases.
:::php
$form = new Form(..);
$form->setFormMethod('POST');
$form->setStrictFormMethodCheck(true);
// or alternative short notation..
$form->setFormMethod('POST', true);
## Spam and Bot Attacks
SilverStripe has no built-in protection for detailing with bots, captcha or other spam protection methods. This
functionality is available as an additional [Spam Protection](https://github.com/silverstripe/silverstripe-spamprotection)
module if required. The module provides an consistent API for allowing third-party spam protection handlers such as
[Recaptcha](http://www.google.com/recaptcha/intro/) and [Mollom](https://mollom.com/) to work within the `Form` API.
## Related Documentation
* [Security](../security)
## API Documentation
* [api:SecurityToken]

View File

@ -1,435 +0,0 @@
# GridField
Gridfield is SilverStripe's implementation of data grids. Its main purpose is to display tabular data
in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks.
It's built in a way that provides developers with an extensible way to display tabular data in a
table and minimise the amount of code that needs to be written.
In order to quickly get data-focused UIs up and running,
you might also be interested in the [/reference/modeladmin](ModelAdmin) class
which is driven largely by the `GridField` class explained here.
## Overview
The `GridField` is a flexible form field for creating tables of data. It was introduced in
SilverStripe 3.0 and replaced the `ComplexTableField`, `TableListField`, and `TableField` from
previous versions of SilverStripe.
Each GridField is built from a number of components. Without any components, a GridField has almost no
functionality. The components are responsible for formatting data to be readable and also modifying it.
A gridfield with only the `GridFieldDataColumn` component will display a set of read-only columns
taken from your list, without any headers or pagination. Large datasets don't fit to one
page, so you could add a `GridFieldPaginator` to paginatate the data. Sorting is supported by adding
a `GridFieldSortableHeader` that enables sorting on fields that can be sorted.
This document aims to explain the usage of GridFields with code examples.
<div class="hint" markdown='1'>
GridField can only be used with datasets that are of the type `SS_List` such as `DataList`
or `ArrayList`
</div>
## Creating a base GridField
A gridfield is often setup from a `Controller` that will output a form to the user. Even if there
are no other HTML input fields for gathering data from users, the gridfield itself must have a
`Form` to support interactions with it.
Here is an example where we display a basic gridfield with the default settings:
:::php
class GridController extends Page_Controller {
private static $allowed_actions = array('index', 'AllPages');
public function index(SS_HTTPRequest $request) {
$this->Content = $this->AllPages();
return $this->render();
}
public function AllPages() {
$gridField = new GridField('pages', 'All pages', SiteTree::get());
return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
}
}
__Note:__ This is example code and the gridfield might not be styled nicely depending on the rest of
the css included.
This gridfield will only contain a single column with the `Title` of each page. Gridfield by default
uses the `DataObject::$display_fields` for guessing what fields to display.
Instead of modifying a core `DataObject` we can tell the gridfield which fields to display by
setting the display fields on the `GridFieldDataColumns` component.
:::php
public function AllPages() {
$gridField = new GridField('pages', 'All pages', SiteTree::get());
$dataColumns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
$dataColumns->setDisplayFields(array(
'Title' => 'Title',
'URLSegment'=> 'URL',
'LastEdited' => 'Changed'
));
return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
}
We will now move onto what the `GridFieldConfig`s are and how to use them.
----
## Configuration
A gridfields's behaviour and look all depends on what config we're giving it. In the above example
we did not specify one, so it picked a default config called `GridFieldConfig_Base`.
A config object is a container for `GridFieldComponents` which contain the actual functionality and
view for the gridfield.
A config object can be either injected as the fourth argument of the GridField constructor,
`$config` or set at a later stage by using a setter:
:::php
// On initialisation:
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
// By a setter after initialisation:
$gridField = new GridField('pages', 'All pages', SiteTree::get());
$gridField->setConfig(GridFieldConfig_Base::create());
By default the `[api:GridFieldConfig_Base]` constructor takes a single parameter to specify the number
of items displayed on each page.
:::php
// I have lots of items, so increase the page size
$myConfig = GridFieldConfig_Base::create(40);
The default page size can also be tweaked via the config. (put in your mysite/_config/config.yml)
:::yaml
// For updating all gridfield defaults system wide
GridFieldPaginator:
default_items_per_page: 40
Note that for [/reference/modeladmin](ModelAdmin) sections the default 30 number of pages can be
controlled either by setting the base `ModelAdmin.page_length` config to the desired number, or
by overriding this value in a custom subclass.
The framework comes shipped with some base GridFieldConfigs:
### Table listing with GridFieldConfig_Base
A simple read-only and paginated view of records with sortable and searchable headers.
:::php
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
The fields displayed are from `DataObject::getSummaryFields()`
### Viewing records with GridFieldConfig_RecordViewer
Similar to `GridFieldConfig_Base` with the addition support of:
- View read-only details of individual records.
The fields displayed in the read-only view is from `DataObject::getCMSFields()`
:::php
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordViewer::create());
### Editing records with GridFieldConfig_RecordEditor
Similar to `GridFieldConfig_RecordViewer` with the addition support of:
- Viewing and changing an individual records data.
- Deleting a record
:::php
$gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordEditor::create());
The fields displayed in the edit form are from `DataObject::getCMSFields()`
### Editing relations with GridFieldConfig_RelationEditor
Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or
many-many relationships. As such, it expects the list used with the `GridField` to be a
`RelationList`. That is, the list returned by a has-many or many-many getter.
The relations can be:
- Searched for existing records and add a relationship
- Detach records from the relationship (rather than removing them from the database)
- Create new related records and automatically add them to the relationship.
:::php
$gridField = new GridField('images', 'Linked images', $this->Images(), GridFieldConfig_RelationEditor::create());
The fields displayed in the edit form are from `DataObject::getCMSFields()`
## Customizing Detail Forms
The `GridFieldDetailForm` component drives the record editing form which is usually configured
through the configs `GridFieldConfig_RecordEditor` and `GridFieldConfig_RelationEditor`
described above. It takes its fields from `DataObject->getCMSFields()`,
but can be customized to accept different fields via its `[api:GridFieldDetailForm->setFields()]` method.
The component also has the ability to load and save data stored on join tables
when two records are related via a "many_many" relationship, as defined through
`[api:DataObject::$many_many_extraFields]`. While loading and saving works transparently,
you need to add the necessary fields manually, they're not included in the `getCMSFields()` scaffolding.
These extra fields act like usual form fields, but need to be "namespaced"
in order for the gridfield logic to detect them as fields for relation extradata,
and to avoid clashes with the other form fields.
The namespace notation is `ManyMany[<extradata-field-name>]`, so for example
`ManyMany[MyExtraField]`.
Example:
:::php
class Player extends DataObject {
private static $db = array('Name' => 'Text');
public static $many_many = array('Teams' => 'Team');
public static $many_many_extraFields = array(
'Teams' => array('Position' => 'Text')
);
public function getCMSFields() {
$fields = parent::getCMSFields();
if($this->ID) {
$teamFields = singleton('Team')->getCMSFields();
$teamFields->addFieldToTab(
'Root.Main',
// Please follow the "ManyMany[<extradata-name>]" convention
new TextField('ManyMany[Position]', 'Current Position')
);
$config = GridFieldConfig_RelationEditor::create();
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
}
return $fields;
}
}
class Team extends DataObject {
private static $db = array('Name' => 'Text');
public static $many_many = array('Players' => 'Player');
}
## GridFieldComponents
The `GridFieldComponent` classes are the actual workers in a gridfield. They can be responsible for:
- Output some HTML to be rendered
- Manipulate data
- Recieve actions
- Display links
Components are added and removed from a config by setters and getters.
:::php
$config = GridFieldConfig::create();
// Add the base data columns to the gridfield
$config->addComponent(new GridFieldDataColumns());
$gridField = new GridField('pages', 'All pages', SiteTree::get(), $config);
It's also possible to insert a component before another component.
:::php
$config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
Adding multiple components in one call:
:::php
$config->addComponents(new GridFieldDataColumns(), new GridFieldToolbarHeader());
Removing a component:
:::php
$config->removeComponentsByType('GridFieldToolbarHeader');
For more information, see the [API for GridFieldConfig](http://api.silverstripe.org/3.0/framework/GridFieldConfig.html).
Here is a list of components for generic use:
- `[api:GridFieldToolbarHeader]`
- `[api:GridFieldSortableHeader]`
- `[api:GridFieldFilterHeader]`
- `[api:GridFieldDataColumns]`
- `[api:GridFieldDeleteAction]`
- `[api:GridFieldViewButton]`
- `[api:GridFieldEditButton]`
- `[api:GridFieldExportButton]`
- `[api:GridFieldPrintButton]`
- `[api:GridFieldPaginator]`
- `[api:GridFieldDetailForm]`
## Flexible Area Assignment through Fragments
GridField layouts can contain many components other than the table itself,
for example a search bar to find existing relations, a button to add those,
and buttons to export and print the current data. The GridField has certain
defined areas called "fragments" where these components can be placed.
The goal is for multiple components to share the same space, for example a header row.
Built-in components:
- `header`/`footer`: Renders in a `<thead>`/`<tfoot>`, should contain table markup
- `before`/`after`: Renders before/after the actual `<table>`
- `buttons-before-left`/`buttons-before-right`/`buttons-after-left`/`buttons-after-right`:
Renders in a shared row before the table. Requires [api:GridFieldButtonRow].
These built-ins can be used by passing the fragment names into the constructor
of various components. Note that some [api:GridFieldConfig] classes
will already have rows added to them. The following example will add a print button
at the bottom right of the table.
:::php
$config->addComponent(new GridFieldButtonRow('after'));
$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
Further down we'll explain how to write your own components using fragments.
## Creating a custom GridFieldComponent
A single component often uses a number of interfaces.
### GridField_HTMLProvider
Provides HTML for the header/footer rows in the table or before/after the template.
Examples:
- A header html provider displays a header before the table
- A pagination html provider displays pagination controls under the table
- A filter html fields displays filter fields on top of the table
- A summary html field displays sums of a field at the bottom of the table
### GridField_ColumnProvider
Add a new column to the table display body, or modify existing columns. Used once per record/row.
Examples:
- A data columns provider that displays data from the list in rows and columns.
- A delete button column provider that adds a delete button at the end of the row
### GridField_ActionProvider
Action providers runs actions, some examples are:
- A delete action provider that deletes a DataObject.
- An export action provider that will export the current list to a CSV file.
### GridField_DataManipulator
Modifies the data list. In general, the data manipulator will make use of `GridState` variables
to decide how to modify the data list.
Examples:
- A paginating data manipulator can apply a limit to a list (show only 20 records)
- A sorting data manipulator can sort the Title in a descending order.
### GridField_URLHandler
Sometimes an action isn't enough, we need to provide additional support URLs for the grid. It
has a list of URL's that it can handle and the GridField passes request on to URLHandlers on matches.
Examples:
- A pop-up form for editing a record's details.
- JSON formatted data used for javascript control of the gridfield.
## GridField_FormAction
This object is used for creating actions buttons, for example a delete button. When a user clicks on
a FormAction, the gridfield finds a `GridField_ActionProvider` that listens on that action.
`GridFieldDeleteAction` have a pretty basic implementation of how to use a Form action.
## GridField_SaveHandler
This is used to create a handler that is called when a form containing the grid
field is saved into a record. This is useful for performing actions when saving
the record.
### GridState
Gridstate is a class that is used to contain the current state and actions on the gridfield. It's
transfered between page requests by being inserted as a hidden field in the form.
A GridFieldComponent sets and gets data from the GridState.
Data within this object can be nested, allowing for organisation of information in a logical fashion. Additionally,
default values can be specified for any data field by invoking that field as a method, passing the default value
as the first parameter. If no default is specified then a nested `GridState_Data` is returned, which may be chained
to subsequently nest data values.
Example:
:::php
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
// Accesses the GridState, returns a nested GridState_Data, and retrieve a single field with a default of null
$objectID = $gridField->State->MyComponent->RecordID(null);
if($objectID) $dataList = $dataList->filter('ParentID', $objectID);
return $dataList;
}
## Permissions
Since GridField is mostly used in the CMS, the controller managing a GridField instance
will already do some permission checks for you, and can decline display or executing
any logic on your field.
If you need more granular control, e.g. to consistently deny non-admins from deleting
records, use the `DataObject->can...()` methods
(see [DataObject permissions](/reference/dataobject#permissions)).
## Creating your own Fragments
Fragments are designated areas within a GridField which can be shared between component templates.
You can define your own fragments by using a `\$DefineFragment' placeholder in your components' template.
This example will simply create an area rendered before the table wrapped in a simple `<div>`.
:::php
class MyAreaComponent implements GridField_HTMLProvider {
public function getHTMLFragments( $gridField) {
return array(
'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
);
}
}
We're returning raw HTML from the component, usually this would be handled by a SilverStripe template.
Please note that in templates, you'll need to escape the dollar sign on `$DefineFragment`:
These are specially processed placeholders as opposed to native template syntax.
Now you can add other components into this area by returning them as an array from
your [api:GridFieldComponent->getHTMLFragments()] implementation:
:::php
class MyShareLinkComponent implements GridField_HTMLProvider {
public function getHTMLFragments( $gridField) {
return array(
'my-area' => '<a href>...</a>'
);
}
}
Your new area can also be used by existing components, e.g. the [api:GridFieldPrintButton]
:::php
new GridFieldPrintButton('my-component-area')
## Related
* [ModelAdmin: A UI driven by GridField](/reference/modeladmin)
* [Tutorial 5: Dataobject Relationship Management](/tutorials/5-dataobject-relationship-management)
* [How to add a custom action to a GridField row](/howto/gridfield-rowaction)

View File

@ -0,0 +1,53 @@
title: Form Transformations
summary: Provide read-only and disabled views of your Form data.
# Read-only and Disabled Forms
[api:Form] and [api:FormField] instances can be turned into a read-only version for things like confirmation pages or
when certain fields cannot be edited due to permissions. Creating the form is done the same way and markup is similar,
`readonly` mode converts the `input`, `select` and `textarea` tags to static HTML elements like `span`.
To make an entire [api:Form] read-only.
:::php
$form = new Form(..);
$form->makeReadonly();
To make all the fields within a [api:FieldList] read-only (i.e to make fields read-only but not buttons).
:::php
$fields = new FieldList(..);
$fields = $fields->makeReadonly();
To make a [api:FormField] read-only you need to know the name of the form field or call it direct on the object
:::php
$field = new TextField(..);
$field = $field->performReadonlyTransformation();
$fields = new FieldList(
$field
);
// Or,
$field = new TextField(..);
$field->setReadonly(true);
$fields = new FieldList(
$field
);
## Disabled FormFields
Disabling [api:FormField] instances, sets the `disabled` property on the class. This will use the same HTML markup as
a normal form, but set the `disabled` attribute on the `input` tag.
:::php
$field = new TextField(..);
$field->setDisabled(true);
echo $field->forTemplate();
// returns '<input type="text" class="text" .. disabled="disabled" />'

View File

@ -0,0 +1,55 @@
title: Tabbed Forms
summary: Find out how CMS interfaces use jQuery UI tabs to provide nested FormFields.
# Tabbed Forms
SilverStripe's [api:FormScaffolder] can automatically generate [api:Form] instances for certain database models. In the
CMS and other scaffolded interfaces, it will output [api:TabSet] and [api:Tab] objects and use jQuery Tabs to split
parts of the data model.
<div class="notice" markdown="1">
All interfaces within the CMS such as [api:ModelAdmin] and [api:LeftAndMain] use tabbed interfaces by default.
</div>
When dealing with tabbed forms, modifying the fields in the form has a few differences. Each [api:Tab] will be given a
name, and normally they all exist under the `Root` [api:TabSet].
<div class="notice" markdown="1">
[api:TabSet] instances can contain child [api:Tab] and further [api:TabSet] instances, however the CMS UI will only
display up to two levels of tabs in the interface. If you want to group data further than that, try [api:ToggleField].
</div>
## Adding a field from a tab.
:::php
$fields->addFieldToTab('Root.Main', new TextField(..));
## Removing a field from a tab
:::php
$fields->removeFieldFromTab('Root.Main', 'Content');
## Creating a new tab
:::php
$fields->addFieldToTab('Root.MyNewTab', new TextField(..));
## Moving a field between tabs
:::php
$field = $fields->dataFieldByName('Content');
$fields->removeFieldFromTab('Root.Main', 'Content');
$fields->addFieldToTab('Root.MyContent', $content);
## Add multiple fields at once
:::php
$fields->addFieldsToTab('Root.Content', array(
TextField::create('Name'),
TextField::create('Email')
));
## API Documentation
* [api:FormScaffolder]

View File

@ -1,6 +1,10 @@
title: Common FormField Types
summary: A table containing a list of the common FormField subclasses.
# Form Field Types # Form Field Types
This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available through our [API] This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available
on the SilverStripe API documentation.
## Basic ## Basic
@ -28,7 +32,7 @@ This is a highlevel overview of available `[api:FormField]` subclasses. An autom
* `[api:DatetimeField]`: Combined date- and time field. * `[api:DatetimeField]`: Combined date- and time field.
* `[api:EmailField]`: Text input field with validation for correct email format according to RFC 2822. * `[api:EmailField]`: Text input field with validation for correct email format according to RFC 2822.
* `[api:GroupedDropdownField]`: Grouped dropdown, using <optgroup> tags. * `[api:GroupedDropdownField]`: Grouped dropdown, using <optgroup> tags.
* `[api:HtmlEditorField]`. * `[api:HtmlEditorField]`: A WYSIWYG editor interface.
* `[api:MoneyField]`: A form field that can save into a `[api:Money]` database field. * `[api:MoneyField]`: A form field that can save into a `[api:Money]` database field.
* `[api:NumericField]`: Text input field with validation for numeric values. * `[api:NumericField]`: Text input field with validation for numeric values.
* `[api:OptionsetField]`: Set of radio buttons designed to emulate a dropdown. * `[api:OptionsetField]`: Set of radio buttons designed to emulate a dropdown.
@ -66,6 +70,6 @@ doesn't necessarily have any visible styling.
* `[api:DatalessField]` - Base class for fields which add some HTML to the form but don't submit any data or * `[api:DatalessField]` - Base class for fields which add some HTML to the form but don't submit any data or
save it to the database save it to the database
* `[api:HeaderField]`: Renders a simple HTML header element. * `[api:HeaderField]`: Renders a simple HTML header element.
* `[api:HiddenField]`. * `[api:HiddenField]` - Renders a hidden input field.
* `[api:LabelField]`: Simple label tag. This can be used to add extra text in your forms. * `[api:LabelField]`: Simple label tag. This can be used to add extra text in your forms.
* `[api:LiteralField]`: Renders arbitrary HTML into a form. * `[api:LiteralField]`: Renders arbitrary HTML into a form.

View File

@ -0,0 +1,130 @@
title: DateField
summary: How to format and use the DateField class.
# DateField
This `FormField` subclass lets you display an editable date, either in a single text input field, or in three separate
fields for day, month and year. It also provides a calendar date picker.
The following example will add a simple DateField to your Page, allowing you to enter a date manually.
**mysite/code/Page.php**
:::php
<?php
class Page extends SiteTree {
private static $db = array(
'MyDate' => 'Date',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab(
'Root.Main',
DateField::create('MyDate', 'Enter a date')
);
return $fields;
}
}
## Custom Date Format
A custom date format for a [api:DateField] can be provided through `setConfig`.
:::php
// will display a date in the following format: 31-06-2012
DateField::create('MyDate')->setConfig('dateformat', 'dd-MM-yyyy');
<div class="info" markdown="1">
The formats are based on [Zend_Date constants](http://framework.zend.com/manual/1.12/en/zend.date.constants.html).
</div>
## Min and Max Dates
Sets the minimum and maximum allowed date values using the `min` and `max` configuration settings (in ISO format or
strtotime()).
:::php
DateField::create('MyDate')
->setConfig('min', '-7 days')
->setConfig('max', '2012-12-31')
## Separate Day / Month / Year Fields
The following setting will display your DateField as three input fields for day, month and year separately. HTML5
placeholders 'day', 'month' and 'year' are enabled by default.
:::php
DateField::create('MyDate')
->setConfig('dmyfields', true)
->setConfig('dmyseparator', '/') // set the separator
->setConfig('dmyplaceholders', 'true'); // enable HTML 5 Placeholders
<div class="alert" markdown="1">
Any custom date format settings will be ignored.
</div>
## Calendar Picker
The following setting will add a Calendar to a single DateField, using the jQuery UI DatePicker widget.
:::php
DateField::create('MyDate')
->setConfig('showcalendar', true);
The jQuery DatePicker doesn't support every constant available for `Zend_Date`. If you choose to use the calendar, the
following constants should at least be safe:
Constant | xxxxx
-------- | -----
d | numeric day of the month (without leading zero)
dd | numeric day of the month (with leading zero)
EEE | dayname, abbreviated
EEEE | dayname
M | numeric month of the year (without leading zero)
MM | numeric month of the year (with leading zero)
MMM | monthname, abbreviated
MMMM | monthname
y | year (4 digits)
yy | year (2 digits)
yyyy | year (4 digits)
Unfortunately the day- and monthname values in Zend Date do not always match those in the existing jQuery UI locale
files, so constants like `EEE` or `MMM`, for day and month names could break validation. To fix this we had to slightly
alter the jQuery locale files, situated in */framework/thirdparty/jquery-ui/datepicker/i18n/*, to match Zend_Date.
<div class="info">
At this moment not all locale files may be present. If a locale file is missing, the DatePicker calendar will fallback
to 'yyyy-MM-dd' whenever day - and/or monthnames are used. After saving, the correct format will be displayed.
</div>
## Formatting Hints
It's often not immediate apparent which format a field accepts, and showing the technical format (e.g. `HH:mm:ss`) is
of limited use to the average user. An alternative is to show the current date in the desired format alongside the
field description as an example.
:::php
$dateField = DateField::create('MyDate');
// Show long format as text below the field
$dateField->setDescription(sprintf(
_t('FormField.Example', 'e.g. %s', 'Example format'),
Convert::raw2xml(Zend_Date::now()->toString($dateField->getConfig('dateformat')))
));
// Alternatively, set short format as a placeholder in the field
$dateField->setAttribute('placeholder', $dateField->getConfig('dateformat'));
<div class="notice" markdown="1">
Fields scaffolded through [api:DataObject::scaffoldCMSFields] automatically have a description attached to them.
</div>
## API Documentation
* [api:DateField]

View File

@ -0,0 +1,394 @@
title: GridField
summary: How to use the GridField class for managing tabular data.
# GridField
[api:GridField] is SilverStripe's implementation of data grids. The main purpose of the `FormField` is to display
tabular data in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks.
:::php
$field = new GridField($name, $title, $list);
<div class="hint" markdown='1'>
GridField can only be used with `$list` data sets that are of the type `SS_List` such as `DataList` or `ArrayList`.
</div>
<div class="notice" markdown="1">
[api:GridField] powers the automated data UI of [api:ModelAdmin]. For more information about `ModelAdmin` see the
[Customizing the CMS](../../customizing_the_cms) guide.
</div>
Each `GridField` is built from a number of components grouped into the [api:GridFieldConfig]. Without any components,
a `GridField` has almost no functionality. The `GridFieldConfig` instance and the attached [api:GridFieldComponent] are
responsible for all the user interactions including formatting data to be readable, modifying data and performing any
actions such as deleting records.
**mysite/code/Page.php**
:::php
<?php
class Page extends SiteTree {
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Pages',
new GridField('Pages', 'All pages', SiteTree::get())
);
return $fields;
}
}
This will display a bare bones `GridField` instance under `Pages` tab in the CMS. As we have not specified the
`GridField` configuration, the default configuration is an instance of [api:GridFieldConfig_Base] which provides:
* [api:GridFieldToolbarHeader]
* [api:GridFieldSortableHeader]
* [api:GridFieldFilterHeader]
* [api:GridFieldDataColumns]
* [api:GridFieldPageCount]
* [api:GridFieldPaginator]
The configuration of those `GridFieldComponent` instances and the addition or subtraction of components is done through
the `getConfig()` method on `GridField`.
**mysite/code/Page.php**
:::php
<?php
class Page extends SiteTree {
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Pages',
$grid = GridField('Pages', 'All pages', SiteTree::get())
);
// GridField configuration
$config = $gridField->getConfig();
//
// Modification of existing components can be done by fetching that component.
// Consult the API documentation for each component to determine the configuration
// you can do.
//
$dataColumns = $config->getComponentByType('GridFieldDataColumns');
$dataColumns->setDisplayFields(array(
'Title' => 'Title',
'Link'=> 'URL',
'LastEdited' => 'Changed'
));
return $fields;
}
}
With the `GridFieldConfig` instance, we can modify the behavior of the `GridField`.
:::php
// `GridFieldConfig::create()` will create an empty configuration (no components).
$config = GridFieldConfig::create();
// add a component
$config->addComponent(new GridFieldDataColumns());
// Update the GridField with our custom configuration
$gridField->setConfig($config);
`GridFieldConfig` provides a number of methods to make setting the configuration easier. We can insert a component
before another component by passing the second parameter.
:::php
$config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
We can add multiple components in one call.
:::php
$config->addComponents(
new GridFieldDataColumns(),
new GridFieldToolbarHeader()
);
Or, remove a component.
:::php
$config->removeComponentsByType('GridFieldDeleteAction');
Fetch a component to modify it later on.
:::php
$component = $config->getComponentByType('GridFieldFilterHeader')
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
modules and extensions.
- [api:GridFieldToolbarHeader]
- [api:GridFieldSortableHeader]
- [api:GridFieldFilterHeader]
- [api:GridFieldDataColumns]
- [api:GridFieldDeleteAction]
- [api:GridFieldViewButton]
- [api:GridFieldEditButton]
- [api:GridFieldExportButton]
- [api:GridFieldPrintButton]
- [api:GridFieldPaginator]
- [api:GridFieldDetailForm]
## Bundled GridFieldConfig
As a shortcut, `GridFieldConfig` subclasses can define a list of `GridFieldComponent` objects to use. This saves
developers manually adding each component.
### GridFieldConfig_Base
A simple read-only and paginated view of records with sortable and searchable headers.
:::php
$config = GridFieldConfig_Base::create();
$gridField->setConfig($config);
// Is the same as adding the following components..
// .. new GridFieldToolbarHeader()
// .. new GridFieldSortableHeader()
// .. new GridFieldFilterHeader()
// .. new GridFieldDataColumns()
// .. new GridFieldPageCount('toolbar-header-right')
// .. new GridFieldPaginator($itemsPerPage)
### GridFieldConfig_RecordViewer
Similar to `GridFieldConfig_Base` with the addition support of the ability to view a `GridFieldDetailForm` containing
a read-only view of the data record.
<div class="info" markdown="1">
The data row show must be a `DataObject` subclass. The fields displayed in the read-only view come from
`DataObject::getCMSFields()`.
</div>
<div class="alert" markdown="1">
The `DataObject` class displayed must define a `canView()` method that returns a boolean on whether the user can view
this record.
</div>
:::php
$config = GridFieldConfig_RecordViewer::create();
$gridField->setConfig($config);
// Same as GridFieldConfig_Base with the addition of
// .. new GridFieldViewButton(),
// .. new GridFieldDetailForm()
### GridFieldConfig_RecordEditor
Similar to `GridFieldConfig_RecordViewer` with the addition support to edit or delete each of the records.
<div class="info" markdown="1">
The data row show must be a `DataObject` subclass. The fields displayed in the edit view come from
`DataObject::getCMSFields()`.
</div>
<div class="alert" markdown="1">
Permission control for editing and deleting the record uses the `canEdit()` and `canDelete()` methods on the
`DataObject` object.
</div>
:::php
$config = GridFieldConfig_RecordEditor::create();
$gridField->setConfig($config);
// Same as GridFieldConfig_RecordViewer with the addition of
// .. new GridFieldAddNewButton(),
// .. new GridFieldEditButton(),
// .. new GridFieldDeleteAction()
### GridFieldConfig_RelationEditor
Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or many-many relationships.
As such, it expects the list used with the `GridField` to be a instance of `RelationList`.
:::php
$config = GridFieldConfig_RelationEditor::create();
$gridField->setConfig($config);
This configuration adds the ability to searched for existing records and add a relationship
(`GridFieldAddExistingAutocompleter`).
Records created or deleted through the `GridFieldConfig_RelationEditor` automatically update the relationship in the
database.
## GridFieldDetailForm
The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from
`DataObject->getCMSFields()` method but can be customized to accept different fields via the
[api:GridFieldDetailForm->setFields] method.
:::php
$form = $gridField->getConfig()->getComponentByType('GridFieldDetailForm');
$form->setFields(new FieldList(
new TextField('Title')
));
### many_many_extraFields
The component also has the ability to load and save data stored on join tables when two records are related via a
"many_many" relationship, as defined through [api:DataObject::$many_many_extraFields]. While loading and saving works
transparently, you need to add the necessary fields manually, they're not included in the `getCMSFields()` scaffolding.
These extra fields act like usual form fields, but need to be "namespaced" in order for the `GridField` logic to detect
them as fields for relation extra data, and to avoid clashes with the other form fields.
The namespace notation is `ManyMany[<extradata-field-name>]`, so for example `ManyMany[MyExtraField]`.
:::php
<?php
class Team extends DataObject {
private static $db = array(
'Name' => 'Text'
);
public static $many_many = array(
'Players' => 'Player'
);
}
class Player extends DataObject {
private static $db = array(
'Name' => 'Text'
);
public static $many_many = array(
'Teams' => 'Team'
);
public static $many_many_extraFields = array(
'Teams' => array(
'Position' => 'Text'
)
);
public function getCMSFields() {
$fields = parent::getCMSFields();
if($this->ID) {
$teamFields = singleton('Team')->getCMSFields();
$teamFields->addFieldToTab(
'Root.Main',
// The "ManyMany[<extradata-name>]" convention
new TextField('ManyMany[Position]', 'Current Position')
);
$config = GridFieldConfig_RelationEditor::create();
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
}
return $fields;
}
}
## Flexible Area Assignment through Fragments
`GridField` layouts can contain many components other than the table itself, for example a search bar to find existing
relations, a button to add those, and buttons to export and print the current data. The `GridField` has certain defined
areas called `fragments` where these components can be placed.
The goal is for multiple components to share the same space, for example a header row. The built-in components:
- `header`/`footer`: Renders in a `<thead>`/`<tfoot>`, should contain table markup
- `before`/`after`: Renders before/after the actual `<table>`
- `buttons-before-left`/`buttons-before-right`/`buttons-after-left`/`buttons-after-right`:
Renders in a shared row before the table. Requires [api:GridFieldButtonRow].
These built-ins can be used by passing the fragment names into the constructor of various components. Note that some
[api:GridFieldConfig] classes will already have rows added to them. The following example will add a print button at the
bottom right of the table.
:::php
$config->addComponent(new GridFieldButtonRow('after'));
$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
### Creating your own Fragments
Fragments are designated areas within a `GridField` which can be shared between component templates. You can define
your own fragments by using a `\$DefineFragment' placeholder in your components' template. This example will simply
create an area rendered before the table wrapped in a simple `<div>`.
:::php
<?php
class MyAreaComponent implements GridField_HTMLProvider {
public function getHTMLFragments( $gridField) {
return array(
'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
);
}
}
<div class="notice" markdown="1">
Please note that in templates, you'll need to escape the dollar sign on `\$DefineFragment`. These are specially
processed placeholders as opposed to native template syntax.
</div>
Now you can add other components into this area by returning them as an array from your
[api:GridFieldComponent->getHTMLFragments()] implementation:
:::php
<?php
class MyShareLinkComponent implements GridField_HTMLProvider {
public function getHTMLFragments( $gridField) {
return array(
'my-area' => '<a href>...</a>'
);
}
}
Your new area can also be used by existing components, e.g. the [api:GridFieldPrintButton]
:::php
new GridFieldPrintButton('my-component-area');
## Creating a Custom GridFieldComponent
Customizing a `GridField` is easy, applications and modules can provide their own `GridFieldComponent` instances to add
functionality. See [How to Create a GridFieldComponent](../how_tos/create_a_gridfield_component).
## Creating a Custom GridField_ActionProvider
[api:GridField_ActionProvider] provides row level actions such as deleting a record. See
[How to Create a GridField_ActionProvider](../how_tos/create_a_gridfield_actionprovider).
## Saving the GridField State
`GridState` is a class that is used to contain the current state and actions on the `GridField`. It's transfered
between page requests by being inserted as a hidden field in the form.
The `GridState_Component` sets and gets data from the `GridState`.
## API Documentation
* [api:GridField]
* [api:GridFieldConfig]
* [api:GridFieldComponent]

View File

@ -0,0 +1,151 @@
title: How to Encapsulate Forms
# How to Encapsulate Forms
Form definitions can often get long, complex and often end up cluttering up a `Controller` definition. We may also want
to reuse the `Form` across multiple `Controller` classes rather than just one. A nice way to encapsulate the logic and
code for a `Form` is to create it as a subclass to `Form`. Let's look at a example of a `Form` which is on our
`Controller` but would be better written as a subclass.
**mysite/code/Page.php**
:::php
<?php
class Page_Controller extends ContentController {
public function SearchForm() {
$fields = new FieldList(
HeaderField::create('Header', 'Step 1. Basics'),
OptionsetField::create('Type', '', array(
'foo' => 'Search Foo',
'bar' => 'Search Bar',
'baz' => 'Search Baz'
)),
CompositeField::create(
HeaderField::create('Header2', 'Step 2. Advanced '),
CheckboxSetField::create('Foo', 'Select Option', array(
'qux' => 'Search Qux'
)),
CheckboxSetField::create('Category', 'Category', array(
'Foo' => 'Foo',
'Bar' => 'Bar'
)),
NumericField::create('Minimum', 'Minimum'),
NumericField::create('Maximum', 'Maximum')
)
);
$actions = new FieldList(
FormAction::create('doSearchForm', 'Search')
);
$required = new RequiredFields(array(
'Type'
));
$form = new Form($this, 'SearchForm', $fields, $actions, $required);
$form->setFormMethod('GET');
$form->addExtraClass('no-action-styles');
$form->disableSecurityToken();
$form->loadDataFrom($_REQUEST);
return $form;
}
..
}
Now that is a bit of code to include on our controller and generally makes the file look much more complex than it
should be. Good practice would be to move this to a subclass and create a new instance for your particular controller.
**mysite/code/forms/SearchForm.php**
:::php
<?php
class SearchForm extends Form {
/**
* Our constructor only requires the controller and the name of the form
* method. We'll create the fields and actions in here.
*
*/
public function __construct($controller, $name) {
$fields = new FieldList(
HeaderField::create('Header', 'Step 1. Basics'),
OptionsetField::create('Type', '', array(
'foo' => 'Search Foo',
'bar' => 'Search Bar',
'baz' => 'Search Baz'
)),
CompositeField::create(
HeaderField::create('Header2', 'Step 2. Advanced '),
CheckboxSetField::create('Foo', 'Select Option', array(
'qux' => 'Search Qux'
)),
CheckboxSetField::create('Category', 'Category', array(
'Foo' => 'Foo',
'Bar' => 'Bar'
)),
NumericField::create('Minimum', 'Minimum'),
NumericField::create('Maximum', 'Maximum')
)
);
$actions = new FieldList(
FormAction::create('doSearchForm', 'Search')
);
$required = new RequiredFields(array(
'Type'
));
// now we create the actual form with our fields and actions defined
// within this class
parent::__construct($controller, $name, $fields, $actions, $required);
// any modifications we need to make to the form.
$this->setFormMethod('GET');
$this->addExtraClass('no-action-styles');
$this->disableSecurityToken();
$this->loadDataFrom($_REQUEST);
}
}
Our controller will now just have to create a new instance of this form object. Keeping the file light and easy to read.
**mysite/code/Page.php**
:::php
<?php
class Page_Controller extends ContentController {
private static $allowed_actions = array(
'SearchForm',
);
public function SearchForm() {
return new SearchForm($this, 'SearchForm');
}
}
Form actions can also be defined within your `Form` subclass to keep the entire form logic encapsulated.
## Related Documentation
* [Introduction to Forms](../introduction)
## API Documentation
* [api:Form]

View File

@ -0,0 +1,52 @@
title: How to Create Lightweight Form
# How to Create Lightweight Form
Out of the box, SilverStripe provides a robust and reusable set of HTML markup for [api:FormFields], however this can
sometimes produce markup which is unnecessarily bloated.
For example, a basic search form. We want to use the [api:Form] API to handle the form but we may want to provide a
totally custom template to meet our needs. To do this, we'll provide the class with a unique template through
`setTemplate`.
**mysite/code/Page.php**
:::php
<?php
public function SearchForm() {
$fields = new FieldList(
TextField::create('q')
);
$actions = new FieldList(
FormAction::create('doSearch', 'Search')
);
$form = new Form($this, 'SearchForm', $fields, $actions);
$form->setTemplate('SearchForm');
return $form;
}
**mysite/templates/Includes/SearchForm.ss**
:::ss
<form $FormAttributes>
<fieldset>
$Fields.dataFieldByName(q)
</fieldset>
<div class="Actions">
<% loop $Actions %>$Field<% end_loop %>
</div>
</form>
`SearchForm.ss` will be executed within the scope of the `Form` object so has access to any of the methods and
properties on [api:Form] such as `$Fields` and `$Actions`.
<div class="notice">
To understand more about Scope or the syntax for custom templates, read the [Templates](../../templates) guide.
</div>

View File

@ -0,0 +1,49 @@
A single component often uses a number of interfaces.
### GridField_HTMLProvider
Provides HTML for the header/footer rows in the table or before/after the template.
Examples:
- A header html provider displays a header before the table
- A pagination html provider displays pagination controls under the table
- A filter html fields displays filter fields on top of the table
- A summary html field displays sums of a field at the bottom of the table
### GridField_ColumnProvider
Add a new column to the table display body, or modify existing columns. Used once per record/row.
Examples:
- A data columns provider that displays data from the list in rows and columns.
- A delete button column provider that adds a delete button at the end of the row
### GridField_ActionProvider
Action providers runs actions, some examples are:
- A delete action provider that deletes a DataObject.
- An export action provider that will export the current list to a CSV file.
### GridField_DataManipulator
Modifies the data list. In general, the data manipulator will make use of `GridState` variables
to decide how to modify the data list.
Examples:
- A paginating data manipulator can apply a limit to a list (show only 20 records)
- A sorting data manipulator can sort the Title in a descending order.
### GridField_URLHandler
Sometimes an action isn't enough, we need to provide additional support URLs for the grid. It
has a list of URL's that it can handle and the GridField passes request on to URLHandlers on matches.
Examples:
- A pop-up form for editing a record's details.
- JSON formatted data used for javascript control of the gridfield.

View File

@ -1,8 +1,22 @@
title: Forms title: Forms
summary: Capture user information through Forms. This guide will work through making forms, custom form fields and adding fields to your data. summary: Capture user information through Forms. This guide will work through how to create SilverStripe forms, adding and modifying fields and how to handle form submissions.
introduction: This guide will work through how to create SilverStripe forms, adding and modifying fields and how to handle form submissions.
[CHILDREN] The [api:Form] class provides a way to create interactive forms in your web application with very little effort.
SilverStripe handles generating the correct semantic HTML markup for the form and each of the fields, as well as the
framework for dealing with submissions and validation.
## How-to [CHILDREN Exclude="How_Tos,Fields"]
[CHILDREN How_To] ## FormField Documentation
[CHILDREN Folder="Fields"]
## How to's
[CHILDREN How_Tos]
## API Documentation
* [api:Form]
* [api:FormField]

View File

@ -1,3 +1,6 @@
title: UploadField
summary: How to use the UploadField class for uploading assets.
# UploadField # UploadField
## Introduction ## Introduction