From 8806b3befc447c7d6e0a26452bdab314b66229c1 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Thu, 20 May 2021 20:07:44 +1200 Subject: [PATCH] Fixes required for dot notation support in fields See #9163 --- .../03_Forms/00_Introduction.md | 2 ++ src/Forms/FieldList.php | 5 ++++- src/Forms/Form.php | 14 ++++++++++---- tests/php/Forms/FormTest.php | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/docs/en/02_Developer_Guides/03_Forms/00_Introduction.md b/docs/en/02_Developer_Guides/03_Forms/00_Introduction.md index fcb6952fa..a2d033127 100644 --- a/docs/en/02_Developer_Guides/03_Forms/00_Introduction.md +++ b/docs/en/02_Developer_Guides/03_Forms/00_Introduction.md @@ -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 Form validation is handled by the [Validator](api:SilverStripe\Forms\Validator) class and the `validator` property on the `Form` object. The validator diff --git a/src/Forms/FieldList.php b/src/Forms/FieldList.php index 5ec25798a..1044b64b3 100644 --- a/src/Forms/FieldList.php +++ b/src/Forms/FieldList.php @@ -511,6 +511,7 @@ class FieldList extends ArrayList */ public function fieldByName($name) { + $fullName = $name; if (strpos($name, '.') !== false) { list($name, $remainder) = explode('.', $name, 2); } else { @@ -518,7 +519,9 @@ class FieldList extends ArrayList } 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 ($child instanceof CompositeField) { return $child->fieldByName($remainder); diff --git a/src/Forms/Form.php b/src/Forms/Form.php index eb9d9b381..c35561d23 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -1463,10 +1463,16 @@ class Form extends ViewableData implements HasRequestHandler if (is_object($data)) { // Allow dot-syntax traversal of has-one relations fields - if (strpos($name, '.') !== false && $data->hasMethod('relField')) { - $val = $data->relField($name); - $exists = true; - + if (strpos($name, '.') !== false) { + $exists = ( + $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 = ( diff --git a/tests/php/Forms/FormTest.php b/tests/php/Forms/FormTest.php index b31a4a254..8ee42df4b 100644 --- a/tests/php/Forms/FormTest.php +++ b/tests/php/Forms/FormTest.php @@ -88,7 +88,8 @@ class FormTest extends FunctionalTest new TextField('key1'), new TextField('namespace[key2]'), new TextField('namespace[key3][key4]'), - new TextField('othernamespace[key5][key6][key7]') + new TextField('othernamespace[key5][key6][key7]'), + new TextField('dot.field') ), new FieldList() ); @@ -108,7 +109,9 @@ class FormTest extends FunctionalTest 'key7' => 'val7' ] ] - ] + ], + 'dot.field' => 'dot.field val' + ]; $form->loadDataFrom($requestData); @@ -118,6 +121,7 @@ class FormTest extends FunctionalTest $this->assertEquals('val2', $fields->fieldByName('namespace[key2]')->Value()); $this->assertEquals('val4', $fields->fieldByName('namespace[key3][key4]')->Value()); $this->assertEquals('val7', $fields->fieldByName('othernamespace[key5][key6][key7]')->Value()); + $this->assertEquals('dot.field val', $fields->fieldByName('dot.field')->Value()); } public function testSubmitReadonlyFields() @@ -186,7 +190,8 @@ class FormTest extends FunctionalTest new TextField('Name'), // appears in both Player and Team new TextareaField('Biography'), new DateField('Birthday'), - new NumericField('BirthdayYear') // dynamic property + new NumericField('BirthdayYear'), // dynamic property + new TextField('FavouriteTeam.Name') // dot syntax ), new FieldList() ); @@ -200,6 +205,7 @@ class FormTest extends FunctionalTest 'Biography' => 'Bio 1', 'Birthday' => '1982-01-01', 'BirthdayYear' => '1982', + 'FavouriteTeam.Name' => 'Team 1', ], 'LoadDataFrom() loads simple fields and dynamic getters' ); @@ -213,6 +219,7 @@ class FormTest extends FunctionalTest 'Biography' => null, 'Birthday' => null, 'BirthdayYear' => 0, + 'FavouriteTeam.Name' => null, ], 'LoadNonBlankDataFrom() loads only fields with values, and doesnt overwrite existing values' ); @@ -229,6 +236,7 @@ class FormTest extends FunctionalTest new TextareaField('Biography'), new DateField('Birthday'), new NumericField('BirthdayYear'), // dynamic property + new TextField('FavouriteTeam.Name'), // dot syntax $unrelatedField = new TextField('UnrelatedFormField') //new CheckboxSetField('Teams') // relation editing ), @@ -245,6 +253,7 @@ class FormTest extends FunctionalTest 'Biography' => 'Bio 1', 'Birthday' => '1982-01-01', 'BirthdayYear' => '1982', + 'FavouriteTeam.Name' => 'Team 1', 'UnrelatedFormField' => 'random value', ], 'LoadDataFrom() doesnt overwrite fields not found in the object' @@ -261,6 +270,7 @@ class FormTest extends FunctionalTest 'Biography' => '', 'Birthday' => '', 'BirthdayYear' => 0, + 'FavouriteTeam.Name' => null, 'UnrelatedFormField' => null, ], 'LoadDataFrom() overwrites fields not found in the object with $clearMissingFields=true'