mirror of
https://github.com/silverstripe/silverstripe-multiform
synced 2024-10-22 11:05:49 +02:00
feat: silverstripe 5 support
This commit is contained in:
parent
27552089c1
commit
3d5995526c
11
.github/workflows/ci.yml
vendored
Normal file
11
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: CI
|
||||
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
|
37
.travis.yml
37
.travis.yml
@ -1,37 +0,0 @@
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
- COMPOSER_ROOT_VERSION=4.0.x-dev
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
|
||||
- php: 7.0
|
||||
env: DB=PGSQL PHPUNIT_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
|
||||
- php: 7.2
|
||||
env: DB=MYSQL PHPUNIT_TEST=1
|
||||
|
||||
before_script:
|
||||
# Init PHP
|
||||
- phpenv rehash
|
||||
- phpenv config-rm xdebug.ini
|
||||
|
||||
# Install composer dependencies
|
||||
- composer validate
|
||||
- composer require --no-update silverstripe/recipe-core:1.0.x-dev
|
||||
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql:2.0.x-dev; fi
|
||||
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
|
||||
|
||||
script:
|
||||
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
|
||||
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=vendor/silverstripe/framework/phpcs.xml.dist src/ tests/; fi
|
||||
|
||||
after_success:
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
34
CHANGELOG
34
CHANGELOG
@ -1,34 +0,0 @@
|
||||
0.3:
|
||||
API CHANGE Changed MultiFormStep::$next_steps to public, and MultiFormStep::$is_final_step to public
|
||||
API CHANGE MultiForm::$start_step is now declared as public, not protected, due to changes in Object::get_static()
|
||||
ENHANCEMENT Custom text for back/next and submit buttons on a per-step basis
|
||||
BUGFIX Checking $this->Data exists before trying to unserialize it
|
||||
BUGFIX #4480 MultiForm::next() and MultiForm::prev() now save the form data before checking the next or previous step
|
||||
API CHANGE Added currentSessionHash property to MultiForm which stores the currently opened session by the user
|
||||
API CHANGE Renamed MultiForm::getSessionRecord() to getCurrentSession() and removed the parameter, since this wasn't done in a very OOP manner
|
||||
ENHANCEMENT: enabled MultiForm to use customised action (actions_exempt_from_validation).
|
||||
Changed has been done in the constructor to read the static variables (incl. overwritten variables).
|
||||
BUGFIX: MultiFormStep::saveInto() needs the current Controller as the new $form Controller to work
|
||||
ENHANCEMENT Added language files
|
||||
BUGFIX If the step doesn't exist, don't attempt to delete it
|
||||
BUGFIX Ensure that any relations to MultiFormStep are destroyed before calling delete()
|
||||
ENHANCEMENT Removed hack of specific action to bypass validation and allow specifying actions to be exempt through a public static variable
|
||||
|
||||
0.2:
|
||||
- ENHANCEMENT Updated entities and added german translation for multiform
|
||||
- ENHANCEMENT Making multiform module translatable
|
||||
- ENHANCEMENT Added MultiFormStep->saveInto() to simulate Form->saveInto()
|
||||
- ENHANCEMENT Made MultiForm->prev() do the same behaviour for saving data
|
||||
- ENHANCEMENT Added MultiForm->getSavedSteps()
|
||||
- BUGFIX Removing url_type which isnt very useful
|
||||
- BUGFIX $this->form wasn't accessible on MultiFormStep
|
||||
- ENHANCEMENT Correct use of parent::construct() so that fields, actions
|
||||
- BUGFIX SQL injection possibility fix on MultiForm->getSessionRecordByID()
|
||||
- BUGFIX Disable security token inherited from Form, which isn't required
|
||||
- BUGFIX Made MultiFormPurgeTask greatly simplified, and workable
|
||||
- ENHANCEMENT Allowed static $ignored_fields to be overloaded on subclass of MultiForm
|
||||
- BUGFIX Use $nextStep->Link and $prevStep->Link() for prev() and next() on MultiForm
|
||||
- API CHANGE Ticket #2562 - Cleaner instanciation of MultiForm subclass without having to call ->init()
|
||||
|
||||
0.1:
|
||||
- initial release
|
355
README.md
355
README.md
@ -6,75 +6,75 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
MultiForm is a SilverStripe module, allowing flow control for forms, and step
|
||||
process to be automatically determined based on configuration variables on each
|
||||
MultiForm is a SilverStripe module, allowing flow control for forms, and step
|
||||
process to be automatically determined based on configuration variables on each
|
||||
step class. It augments the existing Form class in SilverStripe.
|
||||
|
||||
The goal of the module is to allow greater flexibility than features, so each
|
||||
The goal of the module is to allow greater flexibility than features, so each
|
||||
individual implementation can be customized to the project requirements.
|
||||
|
||||
## Maintainer Contact
|
||||
|
||||
* Sean Harvey (Nickname: sharvey, halkyon) <sean (at) silverstripe (dot) com>
|
||||
* Ingo Schommer (Nickname: chillu) <ingo (at) silverstripe (dot) com>
|
||||
- Sean Harvey (Nickname: sharvey, halkyon) <sean (at) silverstripe (dot) com>
|
||||
- Ingo Schommer (Nickname: chillu) <ingo (at) silverstripe (dot) com>
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe ^4.0
|
||||
- SilverStripe ^5.
|
||||
|
||||
**Note:** For a SilverStripe 3.x compatible version, please use [the 1.x release line](https://github.com/silverstripe/silverstripe-multiform/tree/1.3).
|
||||
**Note:** For a SilverStripe 4.x or 3.x compatible version, please use `^2` or `^1` tagged version
|
||||
|
||||
## What it does do
|
||||
|
||||
* 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.
|
||||
- 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.
|
||||
|
||||
## What it doesn't do
|
||||
|
||||
* 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)
|
||||
- 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)
|
||||
|
||||
Note: The *multiform* directory should sit in your SilverStripe root project
|
||||
Note: The _multiform_ directory should sit in your SilverStripe root project
|
||||
directory in the file system as a sibling of cms and framework.
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
This helps to ensure we release less buggy software in the future!
|
||||
|
||||
## Tutorial
|
||||
|
||||
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
|
||||
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
|
||||
before.
|
||||
|
||||
If you are not familiar with SilverStripe, it is highly recommended you run
|
||||
If you are not familiar with SilverStripe, it is highly recommended you run
|
||||
through the tutorials before attempting to start with this one.
|
||||
|
||||
* [View a listing of all available tutorials](http://doc.silverstripe.org/tutorials)
|
||||
- [View a listing of all available tutorials](http://doc.silverstripe.org/tutorials)
|
||||
|
||||
### 1. Installing
|
||||
|
||||
@ -88,14 +88,14 @@ composer require silverstripe/multiform
|
||||
|
||||
### 2. Create subclass of MultiForm
|
||||
|
||||
First of all, we need to create a new subclass of *MultiForm*.
|
||||
First of all, we need to create a new subclass of _MultiForm_.
|
||||
|
||||
For the above example, our multi-form will be called *SurveyForm*
|
||||
For the above example, our multi-form will be called _SurveyForm_
|
||||
|
||||
```php
|
||||
use SilverStripe\MultiForm\Forms\MultiForm;
|
||||
|
||||
class SurveyForm extends MultiForm
|
||||
class SurveyForm extends MultiForm
|
||||
{
|
||||
|
||||
}
|
||||
@ -103,14 +103,14 @@ class SurveyForm extends MultiForm
|
||||
|
||||
### 3. Set up first step
|
||||
|
||||
Now that we've created our new MultiForm subclass step, we need to define what
|
||||
Now that we've created our new MultiForm subclass step, we need to define what
|
||||
form is going to be the first step in our multi-step process.
|
||||
|
||||
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
|
||||
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
|
||||
form.
|
||||
|
||||
So, for example, if we were going to have a first step which collects the
|
||||
So, for example, if we were going to have a first step which collects the
|
||||
personal details of the form user, then we might have this class:
|
||||
|
||||
```php
|
||||
@ -122,8 +122,8 @@ class PersonalDetailsStep extends MultiFormStep
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
is the first step.
|
||||
|
||||
```php
|
||||
@ -137,14 +137,14 @@ class SurveyForm extends MultiForm
|
||||
|
||||
### 4. Define next step, and final step
|
||||
|
||||
We've managed to set up the basics of multi-form, but it's not very useful, as
|
||||
We've managed to set up the basics of multi-form, but it's not very useful, as
|
||||
there's only one step!
|
||||
|
||||
To get more than one step, each step needs to know what it's next step is in
|
||||
To get more than one step, each step needs to know what it's next step is in
|
||||
order to use flow control in our system.
|
||||
|
||||
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`.
|
||||
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`.
|
||||
|
||||
```php
|
||||
use SilverStripe\MultiForm\Models\MultiFormStep;
|
||||
@ -165,12 +165,12 @@ class PersonalDetailsStep extends MultiFormStep
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
will render for the given step.
|
||||
|
||||
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
|
||||
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
|
||||
this is the final step.
|
||||
|
||||
So, if we assume that the last step in our process is OrganisationDetailsStep, then we can do something like this:
|
||||
@ -181,24 +181,24 @@ use SilverStripe\MultiForm\Models\MultiFormStep;
|
||||
class OrganisationDetailsStep extends MultiFormStep
|
||||
{
|
||||
private static $is_final_step = true;
|
||||
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Run database integrity check
|
||||
|
||||
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
|
||||
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
|
||||
with all the latest tables. So you can go ahead and do that.
|
||||
|
||||
*Note: Whenever you add a new step, you **MUST** run dev/build?flush=1 or you
|
||||
may receive errors.*
|
||||
_Note: Whenever you add a new step, you **MUST** run dev/build?flush=1 or you
|
||||
may receive errors._
|
||||
|
||||
However, we've forgotten one thing. We need to create a method on a page-type so
|
||||
However, we've forgotten one thing. We need to create a method on a page-type so
|
||||
that the form can be rendered into a given template.
|
||||
|
||||
So, if we want to render our multi-form as `$SurveyForm` in the *Page.ss*
|
||||
So, if we want to render our multi-form as `$SurveyForm` in the _Page.ss_
|
||||
template, we need to create a SurveyForm method (function) on the controller:
|
||||
|
||||
```php
|
||||
@ -227,114 +227,90 @@ class PageController extends ContentController
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
as the session.
|
||||
|
||||
You can of course, put the *SurveyForm* method on any controller class you
|
||||
You can of course, put the _SurveyForm_ method on any controller class you
|
||||
like.
|
||||
|
||||
Your template should look something like this, to render the form in:
|
||||
|
||||
```html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
<% if $Content %> $Content <% end_if %> <% if $SurveyForm %> $SurveyForm <%
|
||||
end_if %> <% if $Form %> $Form <% end_if %>
|
||||
</div>
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
standard forms are still able to be used alongside our multi-step form.
|
||||
|
||||
### 6. Adding a step indicator
|
||||
|
||||
By default, we include a couple of basic progress indicators which could be
|
||||
By default, we include a couple of basic progress indicators which could be
|
||||
useful, out of the box.
|
||||
|
||||
Two of them, as of the time of writing this are:
|
||||
|
||||
* Progress list (multiform/templates/Includes/MultiFormProgressList.ss)
|
||||
* Progress complete percent (multiform/templates/Includes/MultiFormProgressPercent.ss)
|
||||
- Progress list (multiform/templates/Includes/MultiFormProgressList.ss)
|
||||
- Progress complete percent (multiform/templates/Includes/MultiFormProgressPercent.ss)
|
||||
|
||||
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
|
||||
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
|
||||
indication of completion status.
|
||||
|
||||
To include these with our instance of multiform, we just need to add an
|
||||
To include these with our instance of multiform, we just need to add an
|
||||
`<% include %>` statement into the template.
|
||||
|
||||
For example:
|
||||
|
||||
```html
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
<% with $SurveyForm %> <% include MultiFormProgressList %> <% end_with %>
|
||||
```
|
||||
|
||||
This means the included template is rendered within the scope of the
|
||||
SurveyForm instance returned, instead of the top level controller context.
|
||||
This means the included template is rendered within the scope of the
|
||||
SurveyForm instance returned, instead of the top level controller context.
|
||||
This gives us the data to show the progression of the steps.
|
||||
|
||||
Putting it together, we might have something looking like this:
|
||||
|
||||
```html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
<% if $Content %> $Content <% end_if %> <% if $SurveyForm %> <% with
|
||||
$SurveyForm %> <% include MultiFormProgressList %> <% end_with %>
|
||||
$SurveyForm <% end_if %> <% if $Form %> $Form <% end_if %>
|
||||
</div>
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
use on the MultiForm would be:
|
||||
|
||||
- `AllStepsLinear()` (which also makes use of `getAllStepsRecursive()` to
|
||||
produce a list of steps)
|
||||
- `getCompletedStepCount()`
|
||||
- `getTotalStepCount()`
|
||||
- `getCompletedPercent()`
|
||||
|
||||
* `AllStepsLinear()` (which also makes use of `getAllStepsRecursive()` to
|
||||
produce a list of steps)
|
||||
* `getCompletedStepCount()`
|
||||
* `getTotalStepCount()`
|
||||
* `getCompletedPercent()`
|
||||
|
||||
The default progress indicators make use of the above functions in the
|
||||
The default progress indicators make use of the above functions in the
|
||||
templates.
|
||||
|
||||
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.
|
||||
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.
|
||||
This new method you create would then become available in the progress indicator
|
||||
template.
|
||||
|
||||
|
||||
### 7. Loading values from other steps
|
||||
|
||||
There are several use cases where you want to pre-populate a value based on the submission value of another step.
|
||||
There are several use cases where you want to pre-populate a value based on the submission value of another step.
|
||||
There are two methods supporting this:
|
||||
|
||||
* `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.
|
||||
- `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.
|
||||
|
||||
Here is an example of how to populate the email address from step 1 in step2 :
|
||||
|
||||
@ -383,35 +359,35 @@ class Step2 extends MultiFormStep
|
||||
### 8. Finishing it up
|
||||
|
||||
Now that we've got a structure set up to collect form data along each step, and
|
||||
progress through successfully, we need to customise what happens at the end of
|
||||
progress through successfully, we need to customise what happens at the end of
|
||||
the last step.
|
||||
|
||||
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
|
||||
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
|
||||
`finish()` to tell it what to do at the end.
|
||||
|
||||
Here is an example of what we could do here:
|
||||
|
||||
```php
|
||||
```php
|
||||
use SilverStripe\MultiForm\Forms\MultiForm;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\EmailField;
|
||||
|
||||
class SurveyForm extends MultiForm
|
||||
{
|
||||
{
|
||||
private static $start_step = PersonalDetailsStep::class;
|
||||
|
||||
|
||||
public function finish($data, $form)
|
||||
{
|
||||
parent::finish($data, $form);
|
||||
|
||||
$steps = DataObject::get(
|
||||
MultiFormStep::class,
|
||||
MultiFormStep::class,
|
||||
"SessionID = {$this->session->ID}"
|
||||
);
|
||||
|
||||
|
||||
if ($steps) {
|
||||
foreach ($steps as $step) {
|
||||
if($step->ClassName == PersonalDetailsStep::class) {
|
||||
@ -450,8 +426,8 @@ class SurveyForm extends MultiForm
|
||||
|
||||
#### 9. Organisation data model
|
||||
|
||||
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
|
||||
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
|
||||
groups in SilverStripe) so we need to create it:
|
||||
|
||||
This example has been chosen as a separate DataObject but you may wish to change
|
||||
@ -467,12 +443,13 @@ class Organisation extends DataObject
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
#### Warning
|
||||
|
||||
If you're dealing with sensitive data, it's best to delete the session and step
|
||||
If you're dealing with sensitive data, it's best to delete the session and step
|
||||
data immediately after the form is successfully submitted.
|
||||
|
||||
You can delete it by calling this method on the finish() for your MultiForm
|
||||
You can delete it by calling this method on the finish() for your MultiForm
|
||||
subclass:
|
||||
|
||||
```php
|
||||
@ -483,45 +460,45 @@ This will also go through each of it's steps and delete them as well.
|
||||
|
||||
## Customising
|
||||
|
||||
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
|
||||
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
|
||||
MultiFormStep!):
|
||||
|
||||
### Templates
|
||||
|
||||
The best part about this system is the freedom to customise each form step
|
||||
The best part about this system is the freedom to customise each form step
|
||||
template.
|
||||
|
||||
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
|
||||
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
|
||||
MultiForm class:
|
||||
|
||||
* $this->getCurrentStep()->class (the current step class)
|
||||
* MultiFormStep
|
||||
* $this->class (your subclass of MultiForm)
|
||||
* MultiForm
|
||||
* Form
|
||||
- $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*.
|
||||
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
|
||||
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
|
||||
a base for your new MembershipForm.ss template in your project templates.
|
||||
|
||||
For more information on this, please [look at the Form documentation](http://doc.silverstripe.org/framework/en/topics/forms#custom-form-templates).
|
||||
|
||||
### getNextStep()
|
||||
|
||||
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
|
||||
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
|
||||
step should be. An example:
|
||||
|
||||
```php
|
||||
@ -542,10 +519,11 @@ class MyStep extends MultiFormStep
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Validation
|
||||
|
||||
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
|
||||
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
|
||||
validation see [:form](http://doc.silverstripe.org/form-validation).
|
||||
|
||||
e.g.
|
||||
@ -569,13 +547,13 @@ class MyStep extends MultiFormStep
|
||||
|
||||
### finish()
|
||||
|
||||
`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
|
||||
`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
|
||||
serializes the last step form data into the database, and that's it.
|
||||
|
||||
`finish()` should be overloaded onto your subclass of *MultiForm*, and
|
||||
`parent::finish()` should be called first, otherwise the last step form data
|
||||
`finish()` should be overloaded onto your subclass of _MultiForm_, and
|
||||
`parent::finish()` should be called first, otherwise the last step form data
|
||||
won't be saved.
|
||||
|
||||
For example:
|
||||
@ -598,27 +576,27 @@ class SurveyForm extends MultiForm
|
||||
if($steps) {
|
||||
foreach ($steps as $step) {
|
||||
// Shows the step data (unserialized by loadData)
|
||||
Debug::show($step->loadData());
|
||||
Debug::show($step->loadData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
respect branching of steps (multiple next steps on a given form step).
|
||||
|
||||
## Best practices
|
||||
|
||||
### Delete session after submission
|
||||
|
||||
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
|
||||
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
|
||||
idea to immediately delete this data after the user has submitted.
|
||||
|
||||
This can be easily achieved by adding the following line at the end of your
|
||||
This can be easily achieved by adding the following line at the end of your
|
||||
`finish()` method on your MultiForm subclass.
|
||||
|
||||
```php
|
||||
@ -627,38 +605,39 @@ $this->session->delete();
|
||||
|
||||
### Expiring old session data
|
||||
|
||||
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
|
||||
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
|
||||
after their creation.
|
||||
|
||||
You can run the task from the URL, by using http://mysite.com/dev/tasks/MultiFormPurgeTask?flush=1
|
||||
|
||||
MultiFormPurgeTask is a subclass of *BuildTask*, so can be run using the [SilverStripe CLI tools](http://doc.silverstripe.org/framework/en/topics/commandline).
|
||||
MultiFormPurgeTask is a subclass of _BuildTask_, so can be run using the [SilverStripe CLI tools](http://doc.silverstripe.org/framework/en/topics/commandline).
|
||||
|
||||
One way of automatically running this on a UNIX based machine is by cron.
|
||||
|
||||
## TODO
|
||||
|
||||
* Code example on how to use `$form->saveInto()` with MultiForm, as it doesn't have all steps in the $form context at `finish()`
|
||||
- Code example on how to use `$form->saveInto()` with MultiForm, as it doesn't have all steps in the $form context at `finish()`
|
||||
|
||||
* 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
|
||||
- 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
|
||||
|
||||
* Possibly allow for different means to persist data, such as the browser session cache instead of the database.
|
||||
- Possibly allow for different means to persist data, such as the browser session cache instead of the database.
|
||||
|
||||
* Different presentation of the URL to identify each step.
|
||||
- Different presentation of the URL to identify each step.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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`
|
||||
- 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.
|
||||
|
||||
- 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`
|
||||
|
||||
## Related
|
||||
|
||||
* [Form](/form)
|
||||
* [Form field types](http://doc.silverstripe.org/form-field-types)
|
||||
- [Form](/form)
|
||||
|
||||
* [Tutorials](/tutorials)
|
||||
* [Tutorial 3 - Forms](http://doc.silverstripe.org/framework/en/tutorials/3-forms)
|
||||
- [Form field types](http://doc.silverstripe.org/form-field-types)
|
||||
|
||||
* [Templates](http://doc.silverstripe.org/framework/en/reference/templates)
|
||||
- [Tutorials](/tutorials)
|
||||
|
||||
- [Tutorial 3 - Forms](http://doc.silverstripe.org/framework/en/tutorials/3-forms)
|
||||
|
||||
- [Templates](http://doc.silverstripe.org/framework/en/reference/templates)
|
||||
|
@ -1 +0,0 @@
|
||||
<?php
|
@ -18,16 +18,14 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"silverstripe/framework": "^4"
|
||||
"silverstripe/framework": "^5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "^3.0",
|
||||
"silverstripe/versioned": "^1"
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
"dev-main": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1,7 +1,10 @@
|
||||
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
|
@ -44,7 +44,7 @@ class MultiFormTest extends FunctionalTest
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
protected function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -122,17 +122,17 @@ class MultiFormTest extends FunctionalTest
|
||||
Config::modify()->set(MultiForm::class, 'get_var', 'SuperSessionID');
|
||||
|
||||
$form = $this->controller->Form();
|
||||
$this->assertContains(
|
||||
$this->assertStringContainsString(
|
||||
'SuperSessionID',
|
||||
$form->config()->get('ignored_fields'),
|
||||
'GET var wasn\'t added to ignored fields'
|
||||
);
|
||||
$this->assertContains(
|
||||
$this->assertStringContainsString(
|
||||
'SuperSessionID',
|
||||
$form->FormAction(),
|
||||
"Form action doesn't contain correct session ID parameter"
|
||||
);
|
||||
$this->assertContains(
|
||||
$this->assertStringContainsString(
|
||||
'SuperSessionID',
|
||||
$form->getCurrentStep()->Link(),
|
||||
"Form step doesn't contain correct session ID parameter"
|
||||
|
Loading…
Reference in New Issue
Block a user