2013-05-28 12:04:20 +02:00
# MultiForm Module
2011-02-12 00:35:58 +01:00
2018-01-10 01:55:39 +01:00
[![Scrutinizer Code Quality ](https://img.shields.io/scrutinizer/g/silverstripe/silverstripe-multiform.svg )](https://scrutinizer-ci.com/g/silverstripe/silverstripe-multiform/?branch=master)
[![Code Coverage ](https://img.shields.io/codecov/c/github/silverstripe/silverstripe-multiform.svg )](https://codecov.io/gh/silverstripe/silverstripe-multiform)
2013-03-29 09:44:24 +01:00
2013-05-28 12:04:20 +02:00
## Introduction
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
MultiForm is a SilverStripe module, allowing flow control for forms, and step
process to be automatically determined based on configuration variables on each
2013-05-28 12:04:20 +02:00
step class. It augments the existing Form class in SilverStripe.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
The goal of the module is to allow greater flexibility than features, so each
2013-05-28 12:04:20 +02:00
individual implementation can be customized to the project requirements.
2011-02-12 00:35:58 +01:00
## Maintainer Contact
2023-09-06 10:35:07 +02:00
- Sean Harvey (Nickname: sharvey, halkyon) < sean ( at ) silverstripe ( dot ) com >
- Ingo Schommer (Nickname: chillu) < ingo ( at ) silverstripe ( dot ) com >
2011-02-12 00:35:58 +01:00
## Requirements
2023-09-06 10:35:07 +02:00
- SilverStripe ^5.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
**Note:** For a SilverStripe 4.x or 3.x compatible version, please use `^2` or `^1` tagged version
2011-02-12 00:35:58 +01:00
2018-01-10 01:55:39 +01:00
## What it does do
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- Abstracts fields, actions and validation to each individual step.
- Maintains flow control automatically, so it knows which steps are ahead and
behind. It also can retrieve an entire step process from start to finish, useful
for a step list.
- Persists data by storing it in the session for each step, once it's
completed. The session is saved into the database.
- Allows customisation of next, previous steps, saving, loading and
finalisation of the entire step process
- Allows for basic ability to branch a step by overloading the next step method
with logic to switch based on a condition (e.g. a checkbox, or dropdown in the
field data).
- Ties a user logged in who is using the step process (if applicable). This
means you can build extended security or logging.
- Basic flexibility on the URL presented to the user when they are using the
forms. By default it stores an encrypted hash of the session in the URL, but you
can reference it by the ID instead. It's recommend that additional security,
such as checking the user who first started the session be applied if you want
to reference by ID.
2011-02-12 00:35:58 +01:00
## What it doesn't do
2023-09-06 10:35:07 +02:00
- Automatically handle relation saving, e.g. MembershipForm manages the Member
- Provide a complete package out of the box (you must write a bit of code using
the tutorial!)
- Automatically determine what to do at the end of the process, and where to
save it
- Provide nicely presented URLs of each step (an enhancement, for the future)
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
Note: The _multiform_ directory should sit in your SilverStripe root project
2013-05-28 12:04:20 +02:00
directory in the file system as a sibling of cms and framework.
2011-02-12 00:35:58 +01:00
## Reporting bugs
2023-09-06 10:35:07 +02:00
If you've found a bug that should be fixed for future releases, then please make
a ticket on https://github.com/silverstripe/silverstripe-multiform/issues.
2013-05-28 12:04:20 +02:00
This helps to ensure we release less buggy software in the future!
2011-02-12 00:35:58 +01:00
## Tutorial
2023-09-06 10:35:07 +02:00
The assumption is the developer who is starting this tutorial has an
intermediate level of knowledge in SilverStripe, understands what "run
dev/build?flush=1" means, and has written some custom PHP code in SilverStripe
2013-05-28 12:04:20 +02:00
before.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
If you are not familiar with SilverStripe, it is highly recommended you run
2013-05-28 12:04:20 +02:00
through the tutorials before attempting to start with this one.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- [View a listing of all available tutorials ](http://doc.silverstripe.org/tutorials )
2011-02-12 00:35:58 +01:00
### 1. Installing
2014-02-03 22:17:02 +01:00
Using [Composer ](https://getcomposer.org/ ), you can install multiform into your
SilverStripe site using this command (while in the directory where your site is
currently located)
2013-05-28 12:04:20 +02:00
2017-09-08 06:11:33 +02:00
```
2018-01-10 01:55:39 +01:00
composer require silverstripe/multiform
2017-09-08 06:11:33 +02:00
```
2011-02-12 00:35:58 +01:00
### 2. Create subclass of MultiForm
2023-09-06 10:35:07 +02:00
First of all, we need to create a new subclass of _MultiForm_ .
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
For the above example, our multi-form will be called _SurveyForm_
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-12 01:51:17 +02:00
use SilverStripe\MultiForm\Forms\MultiForm;
2017-09-11 05:24:37 +02:00
2023-09-06 10:35:07 +02:00
class SurveyForm extends MultiForm
2018-01-10 01:55:39 +01:00
{
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
}
```
2011-02-12 00:35:58 +01:00
### 3. Set up first step
2023-09-06 10:35:07 +02:00
Now that we've created our new MultiForm subclass step, we need to define what
2013-05-28 12:04:20 +02:00
form is going to be the first step in our multi-step process.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
Each form step must subclass MultiFormStep. This is so the multi-form can
identify that this step is part of our step process, and is not just a standard
2013-05-28 12:04:20 +02:00
form.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
So, for example, if we were going to have a first step which collects the
2013-05-28 12:04:20 +02:00
personal details of the form user, then we might have this class:
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\MultiForm\Models\MultiFormStep;
class PersonalDetailsStep extends MultiFormStep
{
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
}
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
Now that we've got our first step of the form defined, we need to go back to our
subclass of MultiForm, SurveyForm, and tell it that SurveyFormPersonalDetailsStep
2013-05-28 12:04:20 +02:00
is the first step.
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-12 01:51:17 +02:00
use SilverStripe\MultiForm\Forms\MultiForm;
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
class SurveyForm extends MultiForm
{
private static $start_step = PersonalDetailsStep::class;
2017-09-06 22:47:35 +02:00
}
```
2011-02-12 00:35:58 +01:00
### 4. Define next step, and final step
2023-09-06 10:35:07 +02:00
We've managed to set up the basics of multi-form, but it's not very useful, as
2013-05-28 12:04:20 +02:00
there's only one step!
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
To get more than one step, each step needs to know what it's next step is in
2013-05-28 12:04:20 +02:00
order to use flow control in our system.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
To let the step know what step is next in the process, we do the same as setting
the `$start_step` variable _SurveyForm_ , but we call it `$next_steps` .
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\MultiForm\Models\MultiFormStep;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
class PersonalDetailsStep extends MultiFormStep
{
private static $next_steps = OrganisationDetailsStep::class;
2014-02-04 02:53:52 +01:00
2017-09-11 05:24:37 +02:00
public function getFields()
{
2017-09-10 10:15:43 +02:00
return FieldList::create(
TextField::create('FirstName', 'First name'),
TextField::create('Surname', 'Surname')
2017-09-06 22:47:35 +02:00
);
}
}
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
At the very least, each step also has to have a `getFields()` method returning
a _FieldSet_ with some form field objects. These are the fields that the form
2013-05-28 12:04:20 +02:00
will render for the given step.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
Keep in mind that our multi-form also requires an end point. This step is the
final one, and needs to have another variable set to let the multi-form system know
2013-05-28 12:04:20 +02:00
this is the final step.
2011-02-12 00:35:58 +01:00
2018-01-10 01:55:39 +01:00
So, if we assume that the last step in our process is OrganisationDetailsStep, then we can do something like this:
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\MultiForm\Models\MultiFormStep;
2011-02-12 00:35:58 +01:00
2017-09-11 05:24:37 +02:00
class OrganisationDetailsStep extends MultiFormStep
{
2017-09-08 04:24:28 +02:00
private static $is_final_step = true;
2023-09-06 10:35:07 +02:00
2018-01-10 01:55:39 +01:00
...
2017-09-06 22:47:35 +02:00
}
```
2011-02-12 00:35:58 +01:00
### 5. Run database integrity check
2023-09-06 10:35:07 +02:00
We need to run _dev/build?flush=1_ now, so that the classes are available to the
SilverStripe manifest builder, and to ensure that the database is up to date
2013-05-28 12:04:20 +02:00
with all the latest tables. So you can go ahead and do that.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
_Note: Whenever you add a new step, you **MUST** run dev/build?flush=1 or you
may receive errors._
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
However, we've forgotten one thing. We need to create a method on a page-type so
2013-05-28 12:04:20 +02:00
that the form can be rendered into a given template.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
So, if we want to render our multi-form as `$SurveyForm` in the _Page.ss_
2014-06-17 05:53:22 +02:00
template, we need to create a SurveyForm method (function) on the controller:
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\CMS\Controllers\ContentController;
2018-01-22 22:51:40 +01:00
use SilverStripe\ORM\FieldType\DBHTMLText;
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
class PageController extends ContentController
{
private static $allowed_actions = [
2017-09-06 22:47:35 +02:00
'SurveyForm',
'finished'
2017-09-11 05:24:37 +02:00
];
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
public function SurveyForm()
{
return SurveyForm::create($this, 'SurveyForm');
2017-09-06 22:47:35 +02:00
}
2017-09-11 05:24:37 +02:00
public function finished()
{
return [
2017-09-06 22:47:35 +02:00
'Title' => 'Thank you for your submission',
2018-01-22 22:51:40 +01:00
'Content' => DBHTMLText::create('< p > You have successfully submitted the form!< / p > ')
2017-09-11 05:24:37 +02:00
];
2017-09-06 22:47:35 +02:00
}
}
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
The `SurveyForm()` function will create a new instance of our subclass of
MultiForm, which in this example, is _SurveyForm_ . This in turn will then set
up all the form fields, actions, and validation available to each step, as well
2013-05-28 12:04:20 +02:00
as the session.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
You can of course, put the _SurveyForm_ method on any controller class you
2013-05-28 12:04:20 +02:00
like.
2011-02-12 00:35:58 +01:00
Your template should look something like this, to render the form in:
2017-09-06 22:47:35 +02:00
```html
< div id = "content" >
2023-09-06 10:35:07 +02:00
< % if $Content %> $Content < % end_if %> < % if $SurveyForm %> $SurveyForm < %
end_if %> < % if $Form %> $Form < % end_if %>
2017-09-06 22:47:35 +02:00
< / div >
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
In this case, the above template example is a _sub-template_ inside the _Layout_
directory for the templates. Note that we have also included `$Form` , so
2013-05-28 12:04:20 +02:00
standard forms are still able to be used alongside our multi-step form.
2011-02-12 00:35:58 +01:00
### 6. Adding a step indicator
2023-09-06 10:35:07 +02:00
By default, we include a couple of basic progress indicators which could be
2013-05-28 12:04:20 +02:00
useful, out of the box.
2011-02-12 00:35:58 +01:00
Two of them, as of the time of writing this are:
2023-09-06 10:35:07 +02:00
- Progress list (multiform/templates/Includes/MultiFormProgressList.ss)
- Progress complete percent (multiform/templates/Includes/MultiFormProgressPercent.ss)
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
They are designed to be used either by themselves, or alongside each other.
For example, the percentage could compliment the progress list to give an
2013-05-28 12:04:20 +02:00
indication of completion status.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
To include these with our instance of multiform, we just need to add an
2013-05-28 12:04:20 +02:00
`<% include %>` statement into the template.
2011-02-12 00:35:58 +01:00
For example:
2017-09-06 22:47:35 +02:00
```html
2023-09-06 10:35:07 +02:00
< % with $SurveyForm %> < % include MultiFormProgressList %> < % end_with %>
2017-09-06 22:47:35 +02:00
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
This means the included template is rendered within the scope of the
SurveyForm instance returned, instead of the top level controller context.
2013-05-28 12:04:20 +02:00
This gives us the data to show the progression of the steps.
2011-02-12 00:35:58 +01:00
Putting it together, we might have something looking like this:
2017-09-06 22:47:35 +02:00
```html
< div id = "content" >
2023-09-06 10:35:07 +02:00
< % if $Content %> $Content < % end_if %> < % if $SurveyForm %> < % with
$SurveyForm %> < % include MultiFormProgressList %> < % end_with %>
$SurveyForm < % end_if %> < % if $Form %> $Form < % end_if %>
2017-09-06 22:47:35 +02:00
< / div >
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
Feel free to play around with the progress indicators. If you need something
specific to your project, just create a new "Include" template inside your own
project templates directory, and include that instead. Some helpful methods to
2013-05-28 12:04:20 +02:00
use on the MultiForm would be:
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- `AllStepsLinear()` (which also makes use of `getAllStepsRecursive()` to
produce a list of steps)
- `getCompletedStepCount()`
- `getTotalStepCount()`
- `getCompletedPercent()`
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
The default progress indicators make use of the above functions in the
2013-05-28 12:04:20 +02:00
templates.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
To use a custom method of your own, simply create a new method on your subclass
of MultiForm. In this example, _SurveyForm_ would be the one to customise.
2013-05-28 12:04:20 +02:00
This new method you create would then become available in the progress indicator
template.
2011-02-12 00:35:58 +01:00
2016-03-04 03:34:32 +01:00
### 7. Loading values from other steps
2023-09-06 10:35:07 +02:00
There are several use cases where you want to pre-populate a value based on the submission value of another step.
2018-01-10 01:55:39 +01:00
There are two methods supporting this:
2016-03-04 03:34:32 +01:00
2023-09-06 10:35:07 +02:00
- `getValueFromOtherStep()` loads any submitted value from another step from the session
- `copyValueFromOtherStep()` saves you the repeated work of adding the same lines of code again and again.
2016-03-04 03:34:32 +01:00
Here is an example of how to populate the email address from step 1 in step2 :
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\MultiForm\Models\MultiFormStep;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\EmailField;
2017-09-06 22:47:35 +02:00
class Step1 extends MultiFormStep
{
2017-09-11 05:24:37 +02:00
private static $next_steps = Step2::class;
2016-03-04 03:34:32 +01:00
2017-09-11 05:24:37 +02:00
public function getFields()
{
2017-09-10 10:15:43 +02:00
return FieldList::create(
EmailField::create('Email', 'Your email')
2017-09-06 22:47:35 +02:00
);
}
}
2017-09-11 05:24:37 +02:00
```
```php
use SilverStripe\MultiForm\Models\MultiFormStep;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\EmailField;
2016-03-04 03:34:32 +01:00
2017-09-06 22:47:35 +02:00
class Step2 extends MultiFormStep
{
2017-09-11 05:24:37 +02:00
private static $next_steps = Step3::class;
2016-03-04 03:34:32 +01:00
2017-09-11 05:24:37 +02:00
public function getFields()
{
2017-09-10 10:15:43 +02:00
$fields = FieldList::create(
EmailField::create('Email', 'E-mail'),
EmailField::create('Email2', 'Verify E-Mail')
2017-09-06 22:47:35 +02:00
);
2016-03-04 03:34:32 +01:00
2017-09-06 22:47:35 +02:00
// set the email field to the input from Step 1
$this->copyValueFromOtherStep($fields, 'Step1', 'Email');
2016-03-04 03:34:32 +01:00
2017-09-06 22:47:35 +02:00
return $fields;
}
}
```
2016-03-04 03:34:32 +01:00
### 8. Finishing it up
2011-02-12 00:35:58 +01:00
2013-05-28 12:04:20 +02:00
Now that we've got a structure set up to collect form data along each step, and
2023-09-06 10:35:07 +02:00
progress through successfully, we need to customise what happens at the end of
2013-05-28 12:04:20 +02:00
the last step.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
On the final step, the `finish()` method is called to finalise all the data
from the steps we completed. This method can be found on _MultiForm_ . However,
we cannot automatically save each step, because we don't know where to save it.
So, we must write some code on our subclass of _MultiForm_ , overloading
2013-05-28 12:04:20 +02:00
`finish()` to tell it what to do at the end.
2011-02-12 00:35:58 +01:00
Here is an example of what we could do here:
2023-09-06 10:35:07 +02:00
```php
2017-09-12 01:51:17 +02:00
use SilverStripe\MultiForm\Forms\MultiForm;
2017-09-11 05:24:37 +02:00
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\EmailField;
class SurveyForm extends MultiForm
2023-09-06 10:35:07 +02:00
{
2017-09-11 05:24:37 +02:00
private static $start_step = PersonalDetailsStep::class;
2023-09-06 10:35:07 +02:00
2017-09-11 05:24:37 +02:00
public function finish($data, $form)
{
2017-09-06 22:47:35 +02:00
parent::finish($data, $form);
2023-09-06 10:57:57 +02:00
$steps = MultiFormStep::get()->filter([
"SessionID" => $this->session->ID
]);
2023-09-06 10:35:07 +02:00
2018-01-10 01:55:39 +01:00
if ($steps) {
foreach ($steps as $step) {
2023-09-06 10:57:57 +02:00
if ($step->ClassName == PersonalDetailsStep::class) {
2017-09-10 10:15:43 +02:00
$member = Member::create();
2017-09-06 22:47:35 +02:00
$data = $step->loadData();
2018-01-10 01:55:39 +01:00
if ($data) {
2017-09-06 22:47:35 +02:00
$member->update($data);
$member->write();
}
}
2021-01-05 17:08:10 +01:00
if ($step->ClassName == OrganisationDetailsStep::class) {
2017-09-10 10:15:43 +02:00
$organisation = Organisation::create();
2017-09-06 22:47:35 +02:00
$data = $step->loadData();
2018-01-10 01:55:39 +01:00
if ($data) {
2017-09-06 22:47:35 +02:00
$organisation->update($data);
2018-01-10 01:55:39 +01:00
if ($member & & $member->ID) {
2017-09-06 22:47:35 +02:00
$organisation->MemberID = $member->ID;
}
$organisation->write();
}
}
// Shows the step data (unserialized by loadData)
// Debug::show($step->loadData());
}
}
$this->controller->redirect($this->controller->Link() . 'finished');
}
}
```
2011-02-12 00:35:58 +01:00
2016-03-04 03:34:32 +01:00
#### 9. Organisation data model
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
The class Organisation is mentioned in the above example but doesn't exist at
the moment (unlike the existing Member() class which looks after the member
2013-05-28 12:04:20 +02:00
groups in SilverStripe) so we need to create it:
2011-02-12 00:35:58 +01:00
2014-06-17 05:53:22 +02:00
This example has been chosen as a separate DataObject but you may wish to change
the code and add the data to the Member class instead.
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\ORM\DataObject;
class Organisation extends DataObject
{
2018-01-10 01:55:39 +01:00
private static $db = [
2017-09-06 22:47:35 +02:00
// Add your Organisation fields here
2018-01-10 01:55:39 +01:00
];
2017-09-06 22:47:35 +02:00
}
```
2023-09-06 10:35:07 +02:00
2011-02-12 00:35:58 +01:00
#### Warning
2023-09-06 10:35:07 +02:00
If you're dealing with sensitive data, it's best to delete the session and step
2013-05-28 12:04:20 +02:00
data immediately after the form is successfully submitted.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
You can delete it by calling this method on the finish() for your MultiForm
2013-05-28 12:04:20 +02:00
subclass:
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
$this->session->delete();
```
2011-02-12 00:35:58 +01:00
This will also go through each of it's steps and delete them as well.
## Customising
2023-09-06 10:35:07 +02:00
Because the multi-form system doesn't magically do everything for you, although
it does provide a sensible set of defaults, it means you need to customise
certain aspects of it. Here are some useful methods that can be customised
(although you can technically overload anything available on MultiForm and
2013-05-28 12:04:20 +02:00
MultiFormStep!):
2011-02-12 00:35:58 +01:00
### Templates
2023-09-06 10:35:07 +02:00
The best part about this system is the freedom to customise each form step
2013-05-28 12:04:20 +02:00
template.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
In order, when you have a page with a multi-form rendering into it, it chooses
which template to render that form in this order, within the context of the
2013-05-28 12:04:20 +02:00
MultiForm class:
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- $this->getCurrentStep()->class (the current step class)
- MultiFormStep
- $this->class (your subclass of MultiForm)
- MultiForm
- Form
More than likely, you'll want the first one to be available when the form
renders. To that effect, you can start placing templates in the
_templates/Includes_ directory for your project. You need to name them the same
as the class name for each step. For example, if you want _MembershipForm_ , a
subclass of _MultiFormStep_ to have it's own template, you would put
_MembershipForm.ss_ into that directory, and run _?flush=1_ .
If you'd like a pre-existing template on how to customise the form step, have a
look at Form.ss that's found within the framework module. Use that template, as
2013-05-28 12:04:20 +02:00
a base for your new MembershipForm.ss template in your project templates.
2011-02-12 00:35:58 +01:00
2014-02-03 22:20:39 +01:00
For more information on this, please [look at the Form documentation ](http://doc.silverstripe.org/framework/en/topics/forms#custom-form-templates ).
2013-05-28 12:04:20 +02:00
2011-02-12 00:35:58 +01:00
### getNextStep()
2023-09-06 10:35:07 +02:00
If you are wanting to override the next step (for example if you want the next step to
be something different based on a user's choice of input during the step) you
can override getNextStep() on any given step to manually override what the next
2013-05-28 12:04:20 +02:00
step should be. An example:
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
class MyStep extends MultiFormStep
2017-09-11 05:24:37 +02:00
{
...
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
public function getNextStep()
{
2017-09-06 22:47:35 +02:00
$data = $this->loadData();
2018-01-10 01:55:39 +01:00
if(isset($data['Gender']) & & $data['Gender'] == 'Male') {
2017-09-11 05:24:37 +02:00
return TestThirdCase1Step::class;
2017-09-06 22:47:35 +02:00
} else {
2017-09-11 05:24:37 +02:00
return TestThirdCase2Step::class;
2017-09-06 22:47:35 +02:00
}
}
2017-09-11 05:24:37 +02:00
...
2017-09-06 22:47:35 +02:00
}
```
2023-09-06 10:35:07 +02:00
2011-02-12 00:35:58 +01:00
### Validation
2023-09-06 10:35:07 +02:00
To define validation on a step-by-step basis, please define getValidator() and
return a Validator object, such as RequiredFields - for more information on form
2014-02-03 22:17:02 +01:00
validation see [:form ](http://doc.silverstripe.org/form-validation ).
2011-02-12 00:35:58 +01:00
e.g.
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
class MyStep extends MultiFormStep
{
2017-09-06 22:47:35 +02:00
...
2017-09-11 05:24:37 +02:00
public function getValidator()
{
2017-09-10 10:15:43 +02:00
return RequiredFields::create(array(
2017-09-06 22:47:35 +02:00
'Name',
'Email'
));
}
...
}
```
2011-02-12 00:35:58 +01:00
### finish()
2023-09-06 10:35:07 +02:00
`finish()` is the final call in the process. At this step, all the form data
would most likely be unserialized, and saved to the database in whatever way the
developer sees fit. By default, we have a `finish()` method on _MultiForm_ which
2013-05-28 12:04:20 +02:00
serializes the last step form data into the database, and that's it.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
`finish()` should be overloaded onto your subclass of _MultiForm_ , and
`parent::finish()` should be called first, otherwise the last step form data
2013-05-28 12:04:20 +02:00
won't be saved.
2011-02-12 00:35:58 +01:00
For example:
2017-09-06 22:47:35 +02:00
```php
2017-09-11 05:24:37 +02:00
use SilverStripe\Dev\Debug;
2017-09-12 01:51:17 +02:00
use SilverStripe\MultiForm\Forms\MultiForm;
2017-09-11 05:24:37 +02:00
use SilverStripe\MultiForm\Models\MultiFormStep;
2017-09-06 22:47:35 +02:00
2017-09-11 05:24:37 +02:00
class SurveyForm extends MultiForm
{
private static $start_step = PersonalDetailsStep::class;
2011-02-12 00:35:58 +01:00
2017-09-11 05:24:37 +02:00
public function finish($data, $form)
{
2017-09-06 22:47:35 +02:00
parent::finish($data, $form);
2017-09-11 05:24:37 +02:00
$steps = MultiFormStep::get()->filter(['SessionID' => $this->session->ID]);
2017-09-06 22:47:35 +02:00
if($steps) {
2018-01-10 01:55:39 +01:00
foreach ($steps as $step) {
2017-09-06 22:47:35 +02:00
// Shows the step data (unserialized by loadData)
2023-09-06 10:35:07 +02:00
Debug::show($step->loadData());
2017-09-06 22:47:35 +02:00
}
}
}
}
```
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
The above is a sample bit of code that simply fetches all the steps in the
database that were saved. Further refinement could include getting steps only
if the Data (serialized raw form data) is set, as the above example doesn't
2013-05-28 12:04:20 +02:00
respect branching of steps (multiple next steps on a given form step).
2011-02-12 00:35:58 +01:00
## Best practices
### Delete session after submission
2023-09-06 10:35:07 +02:00
If you're dealing with sensitive data, such as credit card fields, or personal
fields that shouldn't be lying around in the session database, then it's a good
2013-05-28 12:04:20 +02:00
idea to immediately delete this data after the user has submitted.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
This can be easily achieved by adding the following line at the end of your
2013-05-28 12:04:20 +02:00
`finish()` method on your MultiForm subclass.
2011-02-12 00:35:58 +01:00
2017-09-06 22:47:35 +02:00
```php
$this->session->delete();
```
2011-02-12 00:35:58 +01:00
### Expiring old session data
2023-09-06 10:35:07 +02:00
Included with the MultiForm module is a class called _MultiFormPurgeTask_ . This
task can be used to purge expired session data on a regular basis. The date of
expiry can be customised, and is given a default of 7 days to delete sessions
2013-05-28 12:04:20 +02:00
after their creation.
2011-02-12 00:35:58 +01:00
2014-10-23 16:44:12 +02:00
You can run the task from the URL, by using http://mysite.com/dev/tasks/MultiFormPurgeTask?flush=1
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
MultiFormPurgeTask is a subclass of _BuildTask_ , so can be run using the [SilverStripe CLI tools ](http://doc.silverstripe.org/framework/en/topics/commandline ).
2011-02-12 00:35:58 +01:00
2014-02-03 22:17:02 +01:00
One way of automatically running this on a UNIX based machine is by cron.
2011-02-12 00:35:58 +01:00
## TODO
2023-09-06 10:35:07 +02:00
- Code example on how to use `$form->saveInto()` with MultiForm, as it doesn't have all steps in the $form context at `finish()`
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- Allowing a user to click a link, and have an email sent to them with the current state, so they can come back and use the form exactly where they left off
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- Possibly allow for different means to persist data, such as the browser session cache instead of the database.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- Different presentation of the URL to identify each step.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- Allow customisation of `prev()` and `next()` on each step. Currently you can only customise for the entire MultiForm subclass. There is a way to customise on a per step basis, which could be described in a small recipe.
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- More detailed explanation, and recipe example on how to make branched multistep forms. For example, clicking a different action takes you to an alternative next step than the one defined in `$next_steps`
2011-02-12 00:35:58 +01:00
## Related
2023-09-06 10:35:07 +02:00
- [Form ](/form )
- [Form field types ](http://doc.silverstripe.org/form-field-types )
- [Tutorials ](/tutorials )
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- [Tutorial 3 - Forms ](http://doc.silverstripe.org/framework/en/tutorials/3-forms )
2011-02-12 00:35:58 +01:00
2023-09-06 10:35:07 +02:00
- [Templates ](http://doc.silverstripe.org/framework/en/reference/templates )