-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9192 from sminnee/fix-9163
NEW: Support dot syntax in form field names
- Loading branch information
Showing
9 changed files
with
354 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
279 changes: 279 additions & 0 deletions
279
docs/en/02_Developer_Guides/03_Forms/How_Tos/06_Handle_Nested_data.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
--- | ||
title: How to handle nested data in forms | ||
summary: Forms can save into arrays, including has_one relations | ||
iconBrand: wpforms | ||
--- | ||
|
||
# How to: Save nested data | ||
|
||
## Overview | ||
|
||
Forms often save into fields `DataObject` records, through [Form::saveInto()](api:Form::saveInto()). | ||
There are a number of ways to save nested data into those records, including their relationships. | ||
|
||
Let's take the following data structure, and walk through different approaches. | ||
|
||
```php | ||
<?php | ||
use SilverStripe\ORM\DataObject; | ||
|
||
class Player extends DataObject | ||
{ | ||
private static $db = [ | ||
'Name' => 'Varchar', | ||
]; | ||
|
||
private static $has_one = [ | ||
'HometownTeam' => Team::class, | ||
]; | ||
|
||
private static $many_many = [ | ||
'Teams' => Team::class, | ||
]; | ||
} | ||
``` | ||
|
||
``` | ||
<?php | ||
use SilverStripe\ORM\DataObject; | ||
class Team extends DataObject | ||
{ | ||
private static $db = [ | ||
'Name' => 'Varchar', | ||
]; | ||
private static $belongs_many_many = [ | ||
'Players' => Player::class, | ||
]; | ||
} | ||
``` | ||
|
||
## Form fields | ||
|
||
Some form fields like [MultiSelectField](api:MultiSelectField) and [CheckboxSetField](api:CheckboxSetField) | ||
support saving lists of identifiers into a relation. Naming the field by the relation name will | ||
trigger the form field to write into the relationship. | ||
|
||
Example: Select teams for an existing player | ||
|
||
```php | ||
<?php | ||
|
||
use SilverStripe\Control\Controller; | ||
use SilverStripe\Forms\CheckboxSetField; | ||
use SilverStripe\Forms\FieldList; | ||
use SilverStripe\Forms\Form; | ||
use SilverStripe\Forms\FormAction; | ||
use SilverStripe\Forms\HiddenField; | ||
use SilverStripe\Forms\RequiredFields; | ||
use SilverStripe\Forms\TextField; | ||
|
||
class MyController extends Controller | ||
{ | ||
private static $allowed_actions = ['Form']; | ||
|
||
private static $url_segment = 'MyController'; | ||
|
||
public function Form() | ||
{ | ||
$player = Player::get()->byID(1); | ||
return Form::create( | ||
$this, | ||
'Form', | ||
FieldList::create([ | ||
TextField::create('Name'), | ||
CheckboxSetField::create('Teams') | ||
->setSource(Team::get()->map()), | ||
HiddenField::create('ID'), | ||
]), | ||
FieldList::create([ | ||
FormAction::create('doSubmitForm', 'Submit') | ||
]), | ||
RequiredFields::create([ | ||
'Name', | ||
'Teams', | ||
'ID', | ||
]) | ||
)->loadDataFrom($player); | ||
} | ||
|
||
public function doSubmitForm($data, $form) | ||
{ | ||
$player = Player::get()->byID($data['ID']); | ||
|
||
// Only works for updating existing records | ||
if (!$player) { | ||
return false; | ||
} | ||
|
||
// Check permissions for the current user. | ||
if (!$player->canEdit()) { | ||
return false; | ||
} | ||
|
||
// Automatically writes Teams() relationship | ||
$form->saveInto($player); | ||
|
||
$form->sessionMessage('Saved!', 'good'); | ||
|
||
return $this->redirectBack(); | ||
} | ||
} | ||
``` | ||
|
||
|
||
## Dot notation | ||
|
||
For single record relationships (e.g. `has_one`), | ||
forms can automatically traverse into this relationship by using dot notation | ||
in the form field name. This also works with custom getters returning | ||
`DataObject` instances. | ||
|
||
Example: Update team name (via a `has_one` relationship) on an existing player. | ||
|
||
```php | ||
<?php | ||
|
||
use SilverStripe\Control\Controller; | ||
use SilverStripe\Forms\FieldList; | ||
use SilverStripe\Forms\Form; | ||
use SilverStripe\Forms\FormAction; | ||
use SilverStripe\Forms\HiddenField; | ||
use SilverStripe\Forms\RequiredFields; | ||
use SilverStripe\Forms\TextField; | ||
|
||
class MyController extends Controller | ||
{ | ||
private static $allowed_actions = ['Form']; | ||
|
||
private static $url_segment = 'MyController'; | ||
|
||
public function Form() | ||
{ | ||
return Form::create( | ||
$this, | ||
'Form', | ||
FieldList::create([ | ||
TextField::create('Name'), | ||
TextField::create('HometownTeam.Name'), | ||
HiddenField::create('ID'), | ||
]), | ||
FieldList::create([ | ||
FormAction::create('doSubmitForm', 'Submit') | ||
]), | ||
RequiredFields::create([ | ||
'Name', | ||
'HometownTeam.Name', | ||
'ID', | ||
]) | ||
); | ||
} | ||
|
||
public function doSubmitForm($data, $form) | ||
{ | ||
$player = Player::get()->byID($data['ID']); | ||
|
||
// Only works for updating existing records | ||
if (!$player) { | ||
return false; | ||
} | ||
|
||
// Check permissions for the current user. | ||
if (!$player->canEdit() || !$player->HometownTeam()->canEdit()) { | ||
return false; | ||
} | ||
|
||
$form->saveInto($player); | ||
|
||
// Write relationships *before* the original object | ||
// to avoid changes being lost when flush() is called after write(). | ||
// CAUTION: This will create a new record if none is set on the relationship. | ||
// This might or might not be desired behaviour. | ||
$player->HometownTeam()->write(); | ||
$player->write(); | ||
|
||
$form->sessionMessage('Saved!', 'good'); | ||
|
||
return $this->redirectBack(); | ||
} | ||
} | ||
``` | ||
|
||
## Array notation | ||
|
||
This is the most advanced technique, since it works with the form submission directly, | ||
rather than relying on form field logic. | ||
|
||
Example: Create one or more new teams for existing player | ||
|
||
``` | ||
<?php | ||
use SilverStripe\Control\Controller; | ||
use SilverStripe\Forms\FieldList; | ||
use SilverStripe\Forms\Form; | ||
use SilverStripe\Forms\FormAction; | ||
use SilverStripe\Forms\HiddenField; | ||
use SilverStripe\Forms\RequiredFields; | ||
use SilverStripe\Forms\TextField; | ||
class MyController extends Controller | ||
{ | ||
private static $allowed_actions = ['Form']; | ||
private static $url_segment = 'MyController'; | ||
public function Form() | ||
{ | ||
$player = Player::get()->byID(1); | ||
return Form::create( | ||
$this, | ||
'Form', | ||
FieldList::create([ | ||
TextField::create('Name'), | ||
// The UI could duplicate this field to allow creating multiple fields | ||
TextField::create('NewTeams[]', 'New Teams'), | ||
HiddenField::create('ID'), | ||
]), | ||
FieldList::create([ | ||
FormAction::create('doSubmitForm', 'Submit') | ||
]), | ||
RequiredFields::create([ | ||
'Name', | ||
'MyTeams[]', | ||
'ID', | ||
]) | ||
)->loadDataFrom($player); | ||
} | ||
public function doSubmitForm($data, $form) | ||
{ | ||
$player = Player::get()->byID($data['ID']); | ||
// Only works for updating existing records | ||
if (!$player) { | ||
return false; | ||
} | ||
// Check permissions for the current user. | ||
// if (!$player->canEdit()) { | ||
// return false; | ||
// } | ||
$form->saveInto($player); | ||
// Manually create teams based on provided data | ||
foreach ($data['NewTeams'] as $teamName) { | ||
// Caution: Requires data validation on model | ||
$team = Team::create()->update(['Name' => $teamName]); | ||
$team->write(); | ||
$player->Teams()->add($team); | ||
} | ||
$form->sessionMessage('Saved!', 'good'); | ||
return $this->redirectBack(); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# 4.9.0 (Unreleased) | ||
|
||
|
||
## New features | ||
|
||
* [Dot notation support in form fields](https://github.com/silverstripe/silverstripe-framework/pull/9192): Save directly into nested has_one relationships (see [docs](/developer_guides/forms/how_tos/handle_nested_data)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.