mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT Add flattenFields() function to iterate over all deeply nested fields in a form
This commit is contained in:
parent
7df3c49ff9
commit
da9c133c1b
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user