mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Form field schema state
See https://github.com/silverstripe/silverstripe-framework/issues/4938
This commit is contained in:
parent
746322a9f1
commit
e1fcc64c41
@ -166,21 +166,6 @@ class FormField extends RequestHandler {
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* The type of front-end component to render the FormField as.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $schemaComponent;
|
||||
|
||||
/**
|
||||
* Structured schema data representing the FormField.
|
||||
* Used to render the FormField as a ReactJS Component on the front-end.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schemaData = [];
|
||||
|
||||
/**
|
||||
* Takes a field name and converts camelcase to spaced words. Also resolves combined field
|
||||
* names with dot syntax to spaced words.
|
||||
|
@ -4,6 +4,29 @@ namespace SilverStripe\Forms\Schema;
|
||||
|
||||
trait FormFieldSchemaTrait {
|
||||
|
||||
/**
|
||||
* The type of front-end component to render the FormField as.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $schemaComponent;
|
||||
|
||||
/**
|
||||
* Structured schema data representing the FormField.
|
||||
* Used to render the FormField as a ReactJS Component on the front-end.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schemaData = [];
|
||||
|
||||
/**
|
||||
* Structured schema state representing the FormField's current data and validation.
|
||||
* Used to render the FormField as a ReactJS Component on the front-end.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schemaState = [];
|
||||
|
||||
/**
|
||||
* Sets the component type the FormField will be rendered as on the front-end.
|
||||
*
|
||||
@ -58,7 +81,7 @@ trait FormFieldSchemaTrait {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getSchemaDataDefaults() {
|
||||
public function getSchemaDataDefaults() {
|
||||
return [
|
||||
'type' => $this->class,
|
||||
'component' => $this->getSchemaComponent(),
|
||||
@ -78,4 +101,63 @@ trait FormFieldSchemaTrait {
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schema data used for rendering the field on the front-end.
|
||||
* Merges the passed array with the current `$schemaData` or {@link getSchemaDataDefaults()}.
|
||||
* Any passed keys that are not defined in {@link getSchemaDataDefaults()} are ignored.
|
||||
* If you want to pass around ad hoc data use the `data` array e.g. pass `['data' => ['myCustomKey' => 'yolo']]`.
|
||||
*
|
||||
* @param array $schemaData - The data to be merged with $this->schemaData.
|
||||
* @return FormField
|
||||
*
|
||||
* @todo Add deep merging of arrays like `data` and `attributes`.
|
||||
*/
|
||||
public function setSchemaState($schemaState = []) {
|
||||
$current = $this->getSchemaState();
|
||||
|
||||
$this->schemaState = array_merge($current, array_intersect_key($schemaState, $current));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the schema state used to render the FormField on the front-end.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSchemaState() {
|
||||
return array_merge($this->getSchemaStateDefaults(), $this->schemaState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the defaults for $schemaState.
|
||||
* The keys defined here are immutable, meaning undefined keys passed to {@link setSchemaState()} are ignored.
|
||||
* Instead the `data` array should be used to pass around ad hoc data.
|
||||
* Includes validation data if the field is associated to a {@link Form},
|
||||
* and {@link Form->validate()} has been called.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSchemaStateDefaults() {
|
||||
$field = $this;
|
||||
$form = $this->getForm();
|
||||
$validator = $form ? $form->getValidator() : null;
|
||||
$errors = $validator ? (array)$validator->getErrors() : [];
|
||||
$messages = array_filter(array_map(function($error) use ($field) {
|
||||
if($error['fieldName'] === $field->getName()) {
|
||||
return [
|
||||
'value' => $error['message'],
|
||||
'type' => $error['messageType']
|
||||
];
|
||||
}
|
||||
}, $errors));
|
||||
|
||||
return [
|
||||
'id' => $this->ID(),
|
||||
'value' => $this->Value(),
|
||||
'valid' => (count($messages) === 0),
|
||||
'messages' => (array)$messages,
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class FormSchema {
|
||||
/**
|
||||
* Gets the current state of this form as a nested array.
|
||||
*
|
||||
* @param From $form
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
public function getState(Form $form) {
|
||||
@ -55,6 +55,17 @@ class FormSchema {
|
||||
'messages' => []
|
||||
];
|
||||
|
||||
foreach ($form->Fields()->dataFields() as $field) {
|
||||
$state['fields'][] = $field->getSchemaState();
|
||||
}
|
||||
|
||||
if($form->Message()) {
|
||||
$state['messages'][] = [
|
||||
'value' => $form->Message(),
|
||||
'type' => $form->MessageType(),
|
||||
];
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
|
@ -278,6 +278,37 @@ class FormFieldTest extends SapphireTest {
|
||||
$schema = $field->getSchemaData();
|
||||
$this->assertEquals(array_key_exists('myCustomKey', $schema), false);
|
||||
}
|
||||
|
||||
public function testGetSchemaState() {
|
||||
$field = new FormField('MyField');
|
||||
$field->setValue('My value');
|
||||
$schema = $field->getSchemaState();
|
||||
$this->assertEquals('My value', $schema['value']);
|
||||
}
|
||||
|
||||
public function testSetSchemaState() {
|
||||
$field = new FormField('MyField');
|
||||
|
||||
// Make sure the user can update values.
|
||||
$field = $field->setSchemaState(['value' => 'My custom value']);
|
||||
$schema = $field->getSchemaState();
|
||||
$this->assertEquals($schema['value'], 'My custom value');
|
||||
|
||||
// Make user the user can't define custom keys on the schema.
|
||||
$field = $field->setSchemaState(['myCustomKey' => 'yolo']);
|
||||
$schema = $field->getSchemaState();
|
||||
$this->assertEquals(array_key_exists('myCustomKey', $schema), false);
|
||||
}
|
||||
|
||||
public function testGetSchemaStateWithFormValidation() {
|
||||
$field = new FormField('MyField');
|
||||
$validator = new RequiredFields('MyField');
|
||||
$form = new Form(new Controller(), 'TestForm', new FieldList($field), new FieldList(), $validator);
|
||||
$validator->validationError('MyField', 'Something is wrong', 'error');
|
||||
$schema = $field->getSchemaState();
|
||||
$this->assertEquals(count($schema['messages']), 1);
|
||||
$this->assertEquals('Something is wrong', $schema['messages'][0]['value']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +55,83 @@ class FormSchemaTest extends SapphireTest {
|
||||
$formSchema = new FormSchema();
|
||||
$expected = [
|
||||
'id' => 'TestForm',
|
||||
'fields' => [],
|
||||
'fields' => [
|
||||
[
|
||||
'id' => 'Form_TestForm_SecurityID',
|
||||
'value' => $form->getSecurityToken()->getValue(),
|
||||
'messages' => [],
|
||||
'valid' => true,
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
'messages' => []
|
||||
];
|
||||
|
||||
$state = $formSchema->getState($form);
|
||||
$this->assertInternalType('array', $state);
|
||||
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($state));
|
||||
}
|
||||
|
||||
public function testGetStateWithFormMessages() {
|
||||
$fields = new FieldList();
|
||||
$actions = new FieldList();
|
||||
$form = new Form(new Controller(), 'TestForm', $fields, $actions);
|
||||
$form->sessionMessage('All saved', 'good');
|
||||
$formSchema = new FormSchema();
|
||||
$expected = [
|
||||
'id' => 'TestForm',
|
||||
'fields' => [
|
||||
[
|
||||
'id' => 'Form_TestForm_SecurityID',
|
||||
'value' => $form->getSecurityToken()->getValue(),
|
||||
'messages' => [],
|
||||
'valid' => true,
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
'messages' => [
|
||||
[
|
||||
'value' => 'All saved',
|
||||
'type' => 'good'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$state = $formSchema->getState($form);
|
||||
$this->assertInternalType('array', $state);
|
||||
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($state));
|
||||
}
|
||||
|
||||
public function testGetStateWithFieldValidationErrors() {
|
||||
$fields = new FieldList(new TextField('Title'));
|
||||
$actions = new FieldList();
|
||||
$validator = new RequiredFields('Title');
|
||||
$form = new Form(new Controller(), 'TestForm', $fields, $actions, $validator);
|
||||
$form->loadDataFrom([
|
||||
'Title' => 'My Title'
|
||||
]);
|
||||
$validator->validationError('Title', 'Title is invalid', 'error');
|
||||
$formSchema = new FormSchema();
|
||||
$expected = [
|
||||
'id' => 'TestForm',
|
||||
'fields' => [
|
||||
[
|
||||
'id' => 'Form_TestForm_Title',
|
||||
'value' => 'My Title',
|
||||
'messages' => [
|
||||
['value' => 'Title is invalid', 'type' => 'error']
|
||||
],
|
||||
'valid' => false,
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'id' => 'Form_TestForm_SecurityID',
|
||||
'value' => $form->getSecurityToken()->getValue(),
|
||||
'messages' => [],
|
||||
'valid' => true,
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
'messages' => []
|
||||
];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user