mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #9192 from sminnee/fix-9163
NEW: Support dot syntax in form field names
This commit is contained in:
commit
ad4e488dcf
@ -332,6 +332,8 @@ class PageController extends ContentController
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See [how_tos/handle_nested_data](How to: Handle nested form data) for more advanced use cases.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
Form validation is handled by the [Validator](api:SilverStripe\Forms\Validator) class and the `validator` property on the `Form` object. The validator
|
Form validation is handled by the [Validator](api:SilverStripe\Forms\Validator) class and the `validator` property on the `Form` object. The validator
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
6
docs/en/04_Changelogs/4.9.0.md
Normal file
6
docs/en/04_Changelogs/4.9.0.md
Normal file
@ -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)).
|
@ -511,6 +511,7 @@ class FieldList extends ArrayList
|
|||||||
*/
|
*/
|
||||||
public function fieldByName($name)
|
public function fieldByName($name)
|
||||||
{
|
{
|
||||||
|
$fullName = $name;
|
||||||
if (strpos($name, '.') !== false) {
|
if (strpos($name, '.') !== false) {
|
||||||
list($name, $remainder) = explode('.', $name, 2);
|
list($name, $remainder) = explode('.', $name, 2);
|
||||||
} else {
|
} else {
|
||||||
@ -518,7 +519,9 @@ class FieldList extends ArrayList
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this as $child) {
|
foreach ($this as $child) {
|
||||||
if (trim($name) == trim($child->getName()) || $name == $child->id) {
|
if (trim($fullName) == trim($child->getName()) || $fullName == $child->id) {
|
||||||
|
return $child;
|
||||||
|
} elseif (trim($name) == trim($child->getName()) || $name == $child->id) {
|
||||||
if ($remainder) {
|
if ($remainder) {
|
||||||
if ($child instanceof CompositeField) {
|
if ($child instanceof CompositeField) {
|
||||||
return $child->fieldByName($remainder);
|
return $child->fieldByName($remainder);
|
||||||
|
@ -1462,19 +1462,39 @@ class Form extends ViewableData implements HasRequestHandler
|
|||||||
$val = null;
|
$val = null;
|
||||||
|
|
||||||
if (is_object($data)) {
|
if (is_object($data)) {
|
||||||
$exists = (
|
// Allow dot-syntax traversal of has-one relations fields
|
||||||
isset($data->$name) ||
|
if (strpos($name, '.') !== false) {
|
||||||
$data->hasMethod($name) ||
|
$exists = (
|
||||||
($data->hasMethod('hasField') && $data->hasField($name))
|
$data->hasMethod('relField')
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
$val = $data->relField($name);
|
||||||
|
} catch (\LogicException $e) {
|
||||||
|
// There's no other way to tell whether the relation actually exists
|
||||||
|
$exists = false;
|
||||||
|
}
|
||||||
|
// Regular ViewableData access
|
||||||
|
} else {
|
||||||
|
$exists = (
|
||||||
|
isset($data->$name) ||
|
||||||
|
$data->hasMethod($name) ||
|
||||||
|
($data->hasMethod('hasField') && $data->hasField($name))
|
||||||
|
);
|
||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$val = $data->__get($name);
|
$val = $data->__get($name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regular array access. Note that dot-syntax not supported here
|
||||||
} elseif (is_array($data)) {
|
} elseif (is_array($data)) {
|
||||||
if (array_key_exists($name, $data)) {
|
if (array_key_exists($name, $data)) {
|
||||||
$exists = true;
|
$exists = true;
|
||||||
$val = $data[$name];
|
$val = $data[$name];
|
||||||
|
// PHP turns the '.'s in POST vars into '_'s
|
||||||
|
} elseif (array_key_exists($altName = str_replace('.', '_', $name), $data)) {
|
||||||
|
$exists = true;
|
||||||
|
$val = $data[$altName];
|
||||||
} elseif (preg_match_all('/(.*)\[(.*)\]/U', $name, $matches)) {
|
} elseif (preg_match_all('/(.*)\[(.*)\]/U', $name, $matches)) {
|
||||||
// If field is in array-notation we need to access nested data
|
// If field is in array-notation we need to access nested data
|
||||||
//discard first match which is just the whole string
|
//discard first match which is just the whole string
|
||||||
|
@ -469,8 +469,18 @@ class FormField extends RequestHandler
|
|||||||
*/
|
*/
|
||||||
public function saveInto(DataObjectInterface $record)
|
public function saveInto(DataObjectInterface $record)
|
||||||
{
|
{
|
||||||
if ($this->name) {
|
$component = $record;
|
||||||
$record->setCastedField($this->name, $this->dataValue());
|
$fieldName = $this->name;
|
||||||
|
|
||||||
|
// Allow for dot syntax
|
||||||
|
if (($pos = strrpos($this->name, '.')) !== false) {
|
||||||
|
$relation = substr($this->name, 0, $pos);
|
||||||
|
$fieldName = substr($this->name, $pos + 1);
|
||||||
|
$component = $record->relObject($relation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fieldName) {
|
||||||
|
$component->setCastedField($fieldName, $this->dataValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,14 +61,17 @@ class FormTemplateHelper
|
|||||||
*/
|
*/
|
||||||
public function generateFieldID($field)
|
public function generateFieldID($field)
|
||||||
{
|
{
|
||||||
|
// Don't include '.'s in IDs, they confused JavaScript
|
||||||
|
$name = str_replace('.', '_', $field->getName());
|
||||||
|
|
||||||
if ($form = $field->getForm()) {
|
if ($form = $field->getForm()) {
|
||||||
return sprintf(
|
return sprintf(
|
||||||
"%s_%s",
|
"%s_%s",
|
||||||
$this->generateFormID($form),
|
$this->generateFormID($form),
|
||||||
Convert::raw2htmlid($field->getName())
|
Convert::raw2htmlid($name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Convert::raw2htmlid($field->getName());
|
return Convert::raw2htmlid($name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1844,6 +1844,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
return $this->components[$componentName];
|
return $this->components[$componentName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The join object can be returned as a component, named for its alias
|
||||||
|
if (isset($this->record[$componentName]) && $this->record[$componentName] === $this->joinRecord) {
|
||||||
|
return $this->record[$componentName];
|
||||||
|
}
|
||||||
|
|
||||||
$schema = static::getSchema();
|
$schema = static::getSchema();
|
||||||
if ($class = $schema->hasOneComponent(static::class, $componentName)) {
|
if ($class = $schema->hasOneComponent(static::class, $componentName)) {
|
||||||
$joinField = $componentName . 'ID';
|
$joinField = $componentName . 'ID';
|
||||||
|
@ -88,7 +88,8 @@ class FormTest extends FunctionalTest
|
|||||||
new TextField('key1'),
|
new TextField('key1'),
|
||||||
new TextField('namespace[key2]'),
|
new TextField('namespace[key2]'),
|
||||||
new TextField('namespace[key3][key4]'),
|
new TextField('namespace[key3][key4]'),
|
||||||
new TextField('othernamespace[key5][key6][key7]')
|
new TextField('othernamespace[key5][key6][key7]'),
|
||||||
|
new TextField('dot.field')
|
||||||
),
|
),
|
||||||
new FieldList()
|
new FieldList()
|
||||||
);
|
);
|
||||||
@ -108,7 +109,9 @@ class FormTest extends FunctionalTest
|
|||||||
'key7' => 'val7'
|
'key7' => 'val7'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
'dot.field' => 'dot.field val'
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$form->loadDataFrom($requestData);
|
$form->loadDataFrom($requestData);
|
||||||
@ -118,6 +121,7 @@ class FormTest extends FunctionalTest
|
|||||||
$this->assertEquals('val2', $fields->fieldByName('namespace[key2]')->Value());
|
$this->assertEquals('val2', $fields->fieldByName('namespace[key2]')->Value());
|
||||||
$this->assertEquals('val4', $fields->fieldByName('namespace[key3][key4]')->Value());
|
$this->assertEquals('val4', $fields->fieldByName('namespace[key3][key4]')->Value());
|
||||||
$this->assertEquals('val7', $fields->fieldByName('othernamespace[key5][key6][key7]')->Value());
|
$this->assertEquals('val7', $fields->fieldByName('othernamespace[key5][key6][key7]')->Value());
|
||||||
|
$this->assertEquals('dot.field val', $fields->fieldByName('dot.field')->Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubmitReadonlyFields()
|
public function testSubmitReadonlyFields()
|
||||||
@ -186,7 +190,8 @@ class FormTest extends FunctionalTest
|
|||||||
new TextField('Name'), // appears in both Player and Team
|
new TextField('Name'), // appears in both Player and Team
|
||||||
new TextareaField('Biography'),
|
new TextareaField('Biography'),
|
||||||
new DateField('Birthday'),
|
new DateField('Birthday'),
|
||||||
new NumericField('BirthdayYear') // dynamic property
|
new NumericField('BirthdayYear'), // dynamic property
|
||||||
|
new TextField('FavouriteTeam.Name') // dot syntax
|
||||||
),
|
),
|
||||||
new FieldList()
|
new FieldList()
|
||||||
);
|
);
|
||||||
@ -200,6 +205,7 @@ class FormTest extends FunctionalTest
|
|||||||
'Biography' => 'Bio 1',
|
'Biography' => 'Bio 1',
|
||||||
'Birthday' => '1982-01-01',
|
'Birthday' => '1982-01-01',
|
||||||
'BirthdayYear' => '1982',
|
'BirthdayYear' => '1982',
|
||||||
|
'FavouriteTeam.Name' => 'Team 1',
|
||||||
],
|
],
|
||||||
'LoadDataFrom() loads simple fields and dynamic getters'
|
'LoadDataFrom() loads simple fields and dynamic getters'
|
||||||
);
|
);
|
||||||
@ -213,6 +219,7 @@ class FormTest extends FunctionalTest
|
|||||||
'Biography' => null,
|
'Biography' => null,
|
||||||
'Birthday' => null,
|
'Birthday' => null,
|
||||||
'BirthdayYear' => 0,
|
'BirthdayYear' => 0,
|
||||||
|
'FavouriteTeam.Name' => null,
|
||||||
],
|
],
|
||||||
'LoadNonBlankDataFrom() loads only fields with values, and doesnt overwrite existing values'
|
'LoadNonBlankDataFrom() loads only fields with values, and doesnt overwrite existing values'
|
||||||
);
|
);
|
||||||
@ -229,6 +236,7 @@ class FormTest extends FunctionalTest
|
|||||||
new TextareaField('Biography'),
|
new TextareaField('Biography'),
|
||||||
new DateField('Birthday'),
|
new DateField('Birthday'),
|
||||||
new NumericField('BirthdayYear'), // dynamic property
|
new NumericField('BirthdayYear'), // dynamic property
|
||||||
|
new TextField('FavouriteTeam.Name'), // dot syntax
|
||||||
$unrelatedField = new TextField('UnrelatedFormField')
|
$unrelatedField = new TextField('UnrelatedFormField')
|
||||||
//new CheckboxSetField('Teams') // relation editing
|
//new CheckboxSetField('Teams') // relation editing
|
||||||
),
|
),
|
||||||
@ -245,6 +253,7 @@ class FormTest extends FunctionalTest
|
|||||||
'Biography' => 'Bio 1',
|
'Biography' => 'Bio 1',
|
||||||
'Birthday' => '1982-01-01',
|
'Birthday' => '1982-01-01',
|
||||||
'BirthdayYear' => '1982',
|
'BirthdayYear' => '1982',
|
||||||
|
'FavouriteTeam.Name' => 'Team 1',
|
||||||
'UnrelatedFormField' => 'random value',
|
'UnrelatedFormField' => 'random value',
|
||||||
],
|
],
|
||||||
'LoadDataFrom() doesnt overwrite fields not found in the object'
|
'LoadDataFrom() doesnt overwrite fields not found in the object'
|
||||||
@ -261,6 +270,7 @@ class FormTest extends FunctionalTest
|
|||||||
'Biography' => '',
|
'Biography' => '',
|
||||||
'Birthday' => '',
|
'Birthday' => '',
|
||||||
'BirthdayYear' => 0,
|
'BirthdayYear' => 0,
|
||||||
|
'FavouriteTeam.Name' => null,
|
||||||
'UnrelatedFormField' => null,
|
'UnrelatedFormField' => null,
|
||||||
],
|
],
|
||||||
'LoadDataFrom() overwrites fields not found in the object with $clearMissingFields=true'
|
'LoadDataFrom() overwrites fields not found in the object with $clearMissingFields=true'
|
||||||
|
Loading…
Reference in New Issue
Block a user