Merge pull request #6472 from silbinarywolf/feat-fieldlist-nestedfields

feat(FieldList): Add nestedFields() function to return a flat array of all deeply nested fields in a FieldList.
This commit is contained in:
Damian Mooyman 2017-12-18 13:43:36 +13:00 committed by GitHub
commit 623daed5ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 190 additions and 4 deletions

View File

@ -58,6 +58,38 @@ class FieldList extends ArrayList
}
}
/**
* Iterate over each field in the current list recursively
*
* @param callable $callback
*/
public function recursiveWalk(callable $callback)
{
$stack = $this->toArray();
while (!empty($stack)) {
/** @var FormField $field */
$field = array_shift($stack);
$callback($field);
if ($field instanceof CompositeField) {
$stack = array_merge($field->getChildren()->toArray(), $stack);
}
}
}
/**
* Return a flattened list of all fields
*
* @return static
*/
public function flattenFields()
{
$fields = [];
$this->recursiveWalk(function (FormField $field) use (&$fields) {
$fields[] = $field;
});
return static::create($fields);
}
/**
* Return a sequential set of all fields that have data. This excludes wrapper composite fields
* as well as heading / help text fields.
@ -66,8 +98,19 @@ class FieldList extends ArrayList
*/
public function dataFields()
{
if (!$this->sequentialSet) {
$this->collateDataFields($this->sequentialSet);
if (empty($this->sequentialSet)) {
$fields = [];
$this->recursiveWalk(function (FormField $field) use (&$fields) {
if (!$field->hasData()) {
return;
}
$name = $field->getName();
if (isset($fields[$name])) {
$this->fieldNameError($field, __FUNCTION__);
}
$fields[$name] = $field;
});
$this->sequentialSet = $fields;
}
return $this->sequentialSet;
}
@ -77,20 +120,76 @@ class FieldList extends ArrayList
*/
public function saveableFields()
{
if (!$this->sequentialSaveableSet) {
$this->collateDataFields($this->sequentialSaveableSet, true);
if (empty($this->sequentialSaveableSet)) {
$fields = [];
$this->recursiveWalk(function (FormField $field) use (&$fields) {
if (!$field->canSubmitValue()) {
return;
}
$name = $field->getName();
if (isset($fields[$name])) {
$this->fieldNameError($field, __FUNCTION__);
}
$fields[$name] = $field;
});
$this->sequentialSaveableSet = $fields;
}
return $this->sequentialSaveableSet;
}
/**
* Return array of all field names
*
* @return array
*/
public function dataFieldNames()
{
return array_keys($this->dataFields());
}
/**
* Trigger an error for duplicate field names
*
* @param FormField $field
* @param $functionName
*/
protected function fieldNameError(FormField $field, $functionName)
{
if ($this->form) {
$errorSuffix = sprintf(
" in your '%s' form called '%s'",
get_class($this->form),
$this->form->getName()
);
} else {
$errorSuffix = '';
}
user_error(
sprintf(
"%s() I noticed that a field called '%s' appears twice%s",
$functionName,
$field->getName(),
$errorSuffix
),
E_USER_ERROR
);
}
protected function flushFieldsCache()
{
$this->sequentialSet = null;
$this->sequentialSaveableSet = null;
}
/**
* @deprecated 4.1..5.0 Please use dataFields or saveableFields
* @param $list
* @param bool $saveableOnly
*/
protected function collateDataFields(&$list, $saveableOnly = false)
{
Deprecation::notice('5.0', 'Please use dataFields or SaveableFields');
if (!isset($list)) {
$list = array();
}

View File

@ -3,7 +3,10 @@
namespace SilverStripe\Forms\Tests;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\ConfirmedPasswordField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\Tab;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\EmailField;
@ -32,6 +35,90 @@ use SilverStripe\Forms\HiddenField;
*/
class FieldListTest extends SapphireTest
{
public function testRecursiveWalk()
{
$fields = array(
new TextField('Name'),
new EmailField('Email'),
new HiddenField('Hidden'),
new LiteralField('Literal', 'Literal content'),
new CompositeField(
new TextField('Day'),
new TextField('Month'),
new TextField('Year')
),
);
$fieldList = new FieldList($fields);
$count = 0;
$fieldList->recursiveWalk(function (FormField $field) use (&$count) {
++$count;
});
$this->assertEquals(8, $count);
}
public function testFlattenFields()
{
$fields = array(
new TextField('Name'),
new EmailField('Email'),
new HiddenField('Hidden'),
new LiteralField('Literal', 'Literal content'),
$composite = new CompositeField(
$day = new TextField('Day'),
$month = new TextField('Month'),
$year = new TextField('Year')
),
);
$fieldList = new FieldList($fields);
array_pop($fields);
array_push($fields, $composite, $day, $month, $year);
$this->assertEquals($fields, $fieldList->flattenFields()->toArray());
}
public function testSaveableFields()
{
$fields = array(
new TextField('Name'),
new EmailField('Email'),
new HiddenField('Hidden'),
new LiteralField('Literal', 'Literal content'),
new CompositeField(
$day = new TextField('Day'),
$month = new TextField('Month'),
$year = new TextField('Year')
),
);
$fieldList = new FieldList($fields);
array_pop($fields);
array_pop($fields);
array_push($fields, $day, $month, $year);
$this->assertEquals($fields, array_values($fieldList->saveableFields()));
}
public function testFieldNames()
{
$fields = array(
new TextField('Name'),
new EmailField('Email'),
new HiddenField('Hidden'),
new LiteralField('Literal', 'Literal content'),
new CompositeField(
$day = new TextField('Day'),
$month = new TextField('Month'),
$year = new TextField('Year')
),
);
$fieldList = new FieldList($fields);
$this->assertEquals(['Name', 'Email', 'Hidden', 'Day', 'Month', 'Year'], $fieldList->dataFieldNames());
}
/**
* Test adding a field to a tab in a set.