Initial Form Field Schema implementation

- Adds FormSchema class
- Adds FormSchema dependency to LeftAndMain via Injector
- Adds schema allowed_action method to LeftAndMain for generating Form schemas
- Adds FormFieldSchemaTrait to for schema getters and setters on FormFields
This commit is contained in:
David Craig 2016-02-22 14:13:35 +13:00 committed by Ingo Schommer
parent 827d989836
commit afccef718c
7 changed files with 276 additions and 4 deletions

View File

@ -1,6 +1,9 @@
---
Name: coreconfig
---
Injector:
FormSchema:
class: SilverStripe\Forms\Schema\FormSchema
Upload:
# Replace an existing file rather than renaming the new one.
replaceFile: false

View File

@ -5,6 +5,8 @@
* @subpackage admin
*/
use SilverStripe\Forms\Schema\FormSchema;
/**
* LeftAndMain is the parent class of all the two-pane views in the CMS.
* If you are wanting to add more areas to the CMS, you can do it by subclassing LeftAndMain.
@ -84,7 +86,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
/**
* @var array
*/
private static $allowed_actions = array(
private static $allowed_actions = [
'index',
'save',
'savetreenode',
@ -97,7 +99,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
'AddForm',
'batchactions',
'BatchActionsForm',
);
'schema',
];
private static $dependencies = [
'schema' => '%$FormSchema'
];
/**
* @config
@ -169,6 +176,15 @@ class LeftAndMain extends Controller implements PermissionProvider {
*/
protected $responseNegotiator;
/**
* Gets a JSON schema representing the current edit form.
*
* @return SS_HTTPResponse
*/
public function schema() {
return $this->schema->getSchema($this->getEditForm());
}
/**
* @param Member $member
* @return boolean

View File

@ -25,6 +25,8 @@
*/
class FormField extends RequestHandler {
use SilverStripe\Forms\Schema\FormFieldSchemaTrait;
/**
* @var Form
*/
@ -80,7 +82,7 @@ class FormField extends RequestHandler {
* @config
* @var array $default_classes The default classes to apply to the FormField
*/
private static $default_classes = array();
private static $default_classes = [];
/**
@ -162,7 +164,22 @@ class FormField extends RequestHandler {
*
* @var array
*/
protected $attributes = array();
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

View File

@ -0,0 +1,81 @@
<?php
namespace SilverStripe\Forms\Schema;
trait FormFieldSchemaTrait {
/**
* Sets the component type the FormField will be rendered as on the front-end.
*
* @param string $componentType
* @return FormField
*/
public function setSchemaComponent($componentType) {
$this->schemaComponent = $componentType;
return $this;
}
/**
* Gets the type of front-end component the FormField will be rendered as.
*
* @return string
*/
public function getSchemaComponent() {
return $this->schemaComponent;
}
/**
* 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 setSchemaData($schemaData = []) {
$current = $this->getSchemaData();
$this->schemaData = array_merge($current, array_intersect_key($schemaData, $current));
return $this;
}
/**
* Gets the schema data used to render the FormField on the front-end.
*
* @return array
*/
public function getSchemaData() {
return array_merge($this->getSchemaDataDefaults(), $this->schemaData);
}
/**
* Gets the defaults for $schemaData.
* The keys defined here are immutable, meaning undefined keys passed to {@link setSchemaData()} are ignored.
* Instead the `data` array should be used to pass around ad hoc data.
*
* @return array
*/
function getSchemaDataDefaults() {
return [
'type' => $this->class,
'component' => $this->getSchemaComponent(),
'id' => $this->ID,
'holder_id' => null,
'name' => $this->getName(),
'title' => $this->Title(),
'source' => null,
'extraClass' => $this->ExtraClass(),
'description' => $this->getDescription(),
'rightTitle' => $this->RightTitle(),
'leftTitle' => $this->LeftTitle(),
'readOnly' => $this->isReadOnly(),
'disabled' => $this->isDisabled(),
'customValidationMessage' => $this->getCustomValidationMessage(),
'attributes' => [],
'data' => [],
];
}
}

56
forms/FormSchema.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace SilverStripe\Forms\Schema;
use Convert;
use Form;
class FormSchema {
/**
* Gets the schema for this form as a nested array.
*
* @param Form $form
* @return string
*/
public function getSchema(Form $form) {
$request = $form->controller()->getRequest();
$params = $request->AllParams();
$schema = [
'name' => $form->getName(),
'id' => isset($params['ID']) ? $params['ID'] : null,
'action' => isset($params['Action']) ? $params['Action'] : null,
'method' => $form->controller()->getRequest()->HttpMethod(),
'schema_url' => $request->getUrl(),
'attributes' => $form->getAttributes(),
'data' => [],
'fields' => [],
'actions' => []
];
foreach ($form->Actions() as $action) {
$schema['actions'][] = $action->getSchemaData();
}
foreach ($form->Fields() as $fieldList) {
foreach ($fieldList->getForm()->fields()->dataFields() as $field) {
$schema['fields'][] = $field->getSchemaData();
}
}
return Convert::raw2json($schema);
}
/**
* Gets the current state of this form as a nested array.
*
* @param From $form
* @return string
*/
public function getState(Form $form) {
$state = ['state' => []];
return Convert::raw2json($state);
}
}

View File

@ -241,6 +241,43 @@ class FormFieldTest extends SapphireTest {
$this->assertArrayHasKey('extended', $field->getAttributes());
}
public function testSetSchemaComponent() {
$field = new FormField('MyField');
$field = $field->setSchemaComponent('MyComponent');
$component = $field->getSchemaComponent();
$this->assertEquals('MyComponent', $component);
}
public function testGetSchemaDataDefaults() {
$field = new FormField('MyField');
$schema = $field->getSchemaDataDefaults();
$this->assertInternalType('array', $schema);
}
public function testGetSchemaData() {
$field = new FormField('MyField');
$schema = $field->getSchemaData();
$this->assertEquals('MyField', $schema['name']);
// Make sure the schema data is up-to-date with object properties.
$field->setName('UpdatedField');
$schema = $field->getSchemaData();
$this->assertEquals($field->getName(), $schema['name']);
}
public function testSetSchemaData() {
$field = new FormField('MyField');
// Make sure the user can update values.
$field = $field->setSchemaData(['name' => 'MyUpdatedField']);
$schema = $field->getSchemaData();
$this->assertEquals($schema['name'], 'MyUpdatedField');
// Make user the user can't define custom keys on the schema.
$field = $field->setSchemaData(['myCustomKey' => 'yolo']);
$schema = $field->getSchemaData();
$this->assertEquals(array_key_exists('myCustomKey', $schema), false);
}
}
/**

View File

@ -0,0 +1,62 @@
<?php
use SilverStripe\Forms\Schema\FormSchema;
class FormSchemaTest extends SapphireTest {
public function testGetSchema() {
$form = new Form(new Controller(), 'TestForm', new FieldList(), new FieldList());
$formSchema = new FormSchema();
$expectedJSON = json_encode([
'name' => 'TestForm',
'id' => null,
'action' => null,
'method' => '',
'schema_url' => '',
'attributes' => [
'id' => 'Form_TestForm',
'action' => 'Controller/TestForm',
'method' => 'POST',
'enctype' => 'application/x-www-form-urlencoded',
'target' => null,
'class' => ''
],
'data' => [],
'fields' => [
[
'type' => "HiddenField",
'component' => null,
'id' => null,
'holder_id' => null,
'name' => 'SecurityID',
'title' => 'Security ID',
'source' => null,
'extraClass' => 'hidden',
'description' => null,
'rightTitle' => null,
'leftTitle' => null,
'readOnly' => false,
'disabled' => false,
'customValidationMessage' => '',
'attributes' => [],
'data' => []
],
],
'actions' => []
]);
$schema = $formSchema->getSchema($form);
$this->assertJsonStringEqualsJsonString($expectedJSON, $schema);
}
public function testGetState() {
$form = new Form(new Controller(), 'TestForm', new FieldList(), new FieldList());
$formSchema = new FormSchema();
$expectedJSON = json_encode(['state' => []]);
$state = $formSchema->getState($form);
$this->assertJsonStringEqualsJsonString($expectedJSON, $state);
}
}