mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
parent
8806b3befc
commit
99c56fc913
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
Loading…
Reference in New Issue
Block a user