Skip to content

Commit

Permalink
Form field schema state
Browse files Browse the repository at this point in the history
  • Loading branch information
chillu committed Mar 1, 2016
1 parent dbdfc1f commit aef9628
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 3 deletions.
8 changes: 8 additions & 0 deletions forms/FormField.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ class FormField extends RequestHandler {
*/
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 = [];

/**
* Takes a field name and converts camelcase to spaced words. Also resolves combined field
* names with dot syntax to spaced words.
Expand Down
61 changes: 60 additions & 1 deletion forms/FormFieldSchemaTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function getSchemaData() {
*
* @return array
*/
function getSchemaDataDefaults() {
public function getSchemaDataDefaults() {
return [
'type' => $this->class,
'component' => $this->getSchemaComponent(),
Expand All @@ -78,4 +78,63 @@ function getSchemaDataDefaults() {
'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' => [],
];
}
}
13 changes: 12 additions & 1 deletion forms/FormSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function getSchema(Form $form) {
/**
* Gets the current state of this form as a nested array.
*
* @param From $form
* @param Form $form
* @return array
*/
public function getState(Form $form) {
Expand All @@ -55,6 +55,17 @@ public function getState(Form $form) {
'messages' => []
];

foreach ($form->Fields()->dataFields() as $field) {
$state['fields'][] = $field->getSchemaState();
}

if($form->Message()) {
$state['messages'][] = [
'value' => $form->Message(),
'type' => $form->MessageType(),
];
}

return $state;
}
}
31 changes: 31 additions & 0 deletions tests/forms/FormFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,37 @@ public function testSetSchemaData() {
$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']);
}
}

/**
Expand Down
78 changes: 77 additions & 1 deletion tests/forms/FormSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,83 @@ public function testGetState() {
$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' => []
];

Expand Down

0 comments on commit aef9628

Please sign in to comment.