mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #5132 from open-sausages/features/form-schema
API Form schema
This commit is contained in:
commit
6d88caa9b3
@ -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
|
||||
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Deals with special form handling in CMS, mainly around
|
||||
* {@link PjaxResponseNegotiator}
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class CMSForm extends Form {
|
||||
|
||||
/**
|
||||
* Route validation error responses through response negotiator,
|
||||
* so they return the correct markup as expected by the requesting client.
|
||||
*/
|
||||
protected function getValidationErrorResponse() {
|
||||
$request = $this->getRequest();
|
||||
$negotiator = $this->getResponseNegotiator();
|
||||
|
||||
if($request->isAjax() && $negotiator) {
|
||||
$this->setupFormErrors();
|
||||
$result = $this->forTemplate();
|
||||
|
||||
return $negotiator->respond($request, array(
|
||||
'CurrentForm' => function() use($result) {
|
||||
return $result;
|
||||
}
|
||||
));
|
||||
} else {
|
||||
return parent::getValidationErrorResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response negotiator
|
||||
* @param ResponseNegotiator $negotiator The response negotiator to use
|
||||
* @return Form The current form
|
||||
*/
|
||||
public function setResponseNegotiator($negotiator) {
|
||||
$this->responseNegotiator = $negotiator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current response negotiator
|
||||
* @return ResponseNegotiator|null
|
||||
*/
|
||||
public function getResponseNegotiator() {
|
||||
return $this->responseNegotiator;
|
||||
}
|
||||
|
||||
}
|
@ -5,12 +5,16 @@
|
||||
* @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.
|
||||
*
|
||||
* This is essentially an abstract class which should be subclassed.
|
||||
* See {@link CMSMain} for a good example.
|
||||
*
|
||||
* @property FormSchema $schema
|
||||
*/
|
||||
class LeftAndMain extends Controller implements PermissionProvider {
|
||||
|
||||
@ -84,7 +88,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
private static $allowed_actions = [
|
||||
'index',
|
||||
'save',
|
||||
'savetreenode',
|
||||
@ -97,7 +101,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
'AddForm',
|
||||
'batchactions',
|
||||
'BatchActionsForm',
|
||||
);
|
||||
'schema',
|
||||
];
|
||||
|
||||
private static $dependencies = [
|
||||
'schema' => '%$FormSchema'
|
||||
];
|
||||
|
||||
/**
|
||||
* @config
|
||||
@ -169,6 +178,80 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
*/
|
||||
protected $responseNegotiator;
|
||||
|
||||
/**
|
||||
* Gets a JSON schema representing the current edit form.
|
||||
*
|
||||
* WARNING: Experimental API.
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function schema($request) {
|
||||
$response = $this->getResponse();
|
||||
$formName = $request->param('ID');
|
||||
|
||||
if(!$this->hasMethod("get{$formName}")) {
|
||||
throw new SS_HTTPResponse_Exception(
|
||||
'Form not found',
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
if(!$this->hasAction($formName)) {
|
||||
throw new SS_HTTPResponse_Exception(
|
||||
'Form not accessible',
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
$form = $this->{"get{$formName}"}();
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
$response->setBody(Convert::raw2json($this->getSchemaForForm($form)));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a representation of the provided {@link Form} as structured data,
|
||||
* based on the request data.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
protected function getSchemaForForm(Form $form) {
|
||||
$request = $this->getRequest();
|
||||
$schemaParts = [];
|
||||
$return = null;
|
||||
|
||||
// Valid values for the "X-Formschema-Request" header are "schema" and "state".
|
||||
// If either of these values are set they will be stored in the $schemaParst array
|
||||
// and used to construct the response body.
|
||||
if ($schemaHeader = $request->getHeader('X-Formschema-Request')) {
|
||||
$schemaParts = array_filter(explode(',', $schemaHeader), function($value) {
|
||||
$validHeaderValues = ['schema', 'state'];
|
||||
return in_array(trim($value), $validHeaderValues);
|
||||
});
|
||||
}
|
||||
|
||||
if (!count($schemaParts)) {
|
||||
throw new SS_HTTPResponse_Exception(
|
||||
'Invalid request. Check you\'ve set a "X-Formschema-Request" header with "schema" or "state" values.',
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
$return = ['id' => $form->getName()];
|
||||
|
||||
if (in_array('schema', $schemaParts)) {
|
||||
$return['schema'] = $this->schema->getSchema($form);
|
||||
}
|
||||
|
||||
if (in_array('state', $schemaParts)) {
|
||||
$return['state'] = $this->schema->getState($form);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
@ -1013,6 +1096,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* Save handler
|
||||
*/
|
||||
public function save($data, $form) {
|
||||
$request = $this->getRequest();
|
||||
$className = $this->stat('tree_class');
|
||||
|
||||
// Existing or new record?
|
||||
@ -1033,7 +1117,16 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
$this->setCurrentPageID($record->ID);
|
||||
|
||||
$this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.SAVEDUP', 'Saved.')));
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
|
||||
if($request->getHeader('X-Formschema-Request')) {
|
||||
$data = $this->getSchemaForForm($form);
|
||||
$response = new SS_HTTPResponse(Convert::raw2json($data));
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
} else {
|
||||
$response = $this->getResponseNegotiator()->respond($request);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function delete($data, $form) {
|
||||
@ -1260,14 +1353,27 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
$actionsFlattened = $actions->dataFields();
|
||||
if($actionsFlattened) foreach($actionsFlattened as $action) $action->setUseButtonTag(true);
|
||||
|
||||
$form = CMSForm::create(
|
||||
$negotiator = $this->getResponseNegotiator();
|
||||
$form = Form::create(
|
||||
$this, "EditForm", $fields, $actions
|
||||
)->setHTMLID('Form_EditForm');
|
||||
$form->setResponseNegotiator($this->getResponseNegotiator());
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
$form->loadDataFrom($record);
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
$form->setAttribute('data-pjax-fragment', 'CurrentForm');
|
||||
$form->setValidationResponseCallback(function() use ($negotiator, $form) {
|
||||
$request = $this->getRequest();
|
||||
if($request->isAjax() && $negotiator) {
|
||||
$form->setupFormErrors();
|
||||
$result = $form->forTemplate();
|
||||
|
||||
return $negotiator->respond($request, array(
|
||||
'CurrentForm' => function() use($result) {
|
||||
return $result;
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// Announce the capability so the frontend can decide whether to allow preview or not.
|
||||
if(in_array('CMSPreviewable', class_implements($record))) {
|
||||
@ -1318,7 +1424,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* @return Form
|
||||
*/
|
||||
public function EmptyForm() {
|
||||
$form = CMSForm::create(
|
||||
$form = Form::create(
|
||||
$this,
|
||||
"EditForm",
|
||||
new FieldList(
|
||||
@ -1337,7 +1443,6 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
),
|
||||
new FieldList()
|
||||
)->setHTMLID('Form_EditForm');
|
||||
$form->setResponseNegotiator($this->getResponseNegotiator());
|
||||
$form->unsetValidator();
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
$form->addExtraClass('root-form');
|
||||
|
@ -138,13 +138,12 @@ abstract class ModelAdmin extends LeftAndMain {
|
||||
$listField->getConfig()->getComponentByType('GridFieldDetailForm')->setValidator($detailValidator);
|
||||
}
|
||||
|
||||
$form = CMSForm::create(
|
||||
$form = Form::create(
|
||||
$this,
|
||||
'EditForm',
|
||||
new FieldList($listField),
|
||||
new FieldList()
|
||||
)->setHTMLID('Form_EditForm');
|
||||
$form->setResponseNegotiator($this->getResponseNegotiator());
|
||||
$form->addExtraClass('cms-edit-form cms-panel-padded center');
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
$editFormAction = Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'EditForm');
|
||||
|
@ -177,13 +177,12 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
$actions = new FieldList();
|
||||
|
||||
$form = CMSForm::create(
|
||||
$form = Form::create(
|
||||
$this,
|
||||
'EditForm',
|
||||
$fields,
|
||||
$actions
|
||||
)->setHTMLID('Form_EditForm');
|
||||
$form->setResponseNegotiator($this->getResponseNegotiator());
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
// Tab nav in CMS is rendered through separate template
|
||||
|
@ -79,6 +79,11 @@ class Form extends RequestHandler {
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* @var callable {@see setValidationResponseCallback()}
|
||||
*/
|
||||
protected $validationResponseCallback;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -479,16 +484,45 @@ class Form extends RequestHandler {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function getValidationResponseCallback() {
|
||||
return $this->validationResponseCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrules validation error behaviour in {@link httpSubmission()}
|
||||
* when validation has failed. Useful for optional handling of a certain accepted content type.
|
||||
*
|
||||
* The callback can opt out of handling specific responses by returning NULL,
|
||||
* in which case the default form behaviour will kick in.
|
||||
*
|
||||
* @param $callback
|
||||
* @return self
|
||||
*/
|
||||
public function setValidationResponseCallback($callback) {
|
||||
$this->validationResponseCallback = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate response up the controller chain
|
||||
* if {@link validate()} fails (which is checked prior to executing any form actions).
|
||||
* By default, returns different views for ajax/non-ajax request, and
|
||||
* handles 'application/json' requests with a JSON object containing the error messages.
|
||||
* Behaviour can be influenced by setting {@link $redirectToFormOnValidationError}.
|
||||
* Behaviour can be influenced by setting {@link $redirectToFormOnValidationError},
|
||||
* and can be overruled by setting {@link $validationResponseCallback}.
|
||||
*
|
||||
* @return SS_HTTPResponse|string
|
||||
*/
|
||||
protected function getValidationErrorResponse() {
|
||||
$callback = $this->getValidationResponseCallback();
|
||||
if($callback && $callbackResponse = $callback()) {
|
||||
return $callbackResponse;
|
||||
}
|
||||
|
||||
$request = $this->getRequest();
|
||||
if($request->isAjax()) {
|
||||
// Special case for legacy Validator.js implementation
|
||||
|
@ -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,7 @@ class FormField extends RequestHandler {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = array();
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* Takes a field name and converts camelcase to spaced words. Also resolves combined field
|
||||
|
174
forms/FormFieldSchemaTrait.php
Normal file
174
forms/FormFieldSchemaTrait.php
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Schema;
|
||||
|
||||
/**
|
||||
* Class FormFieldSchemaTrait
|
||||
* @package SilverStripe\Forms\Schema
|
||||
*
|
||||
* Allows {@link FormField} to be represented as structured data,
|
||||
* including both structure (name, id, attributes, etc.) and state (field value).
|
||||
* Can be used by {@link FormSchema} to represent a form in JSON,
|
||||
* to be consumed by a front-end application.
|
||||
*
|
||||
* WARNING: Experimental API.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public 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' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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' => [],
|
||||
];
|
||||
}
|
||||
}
|
79
forms/FormSchema.php
Normal file
79
forms/FormSchema.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Schema;
|
||||
|
||||
use Convert;
|
||||
use Form;
|
||||
|
||||
/**
|
||||
* Class FormSchema
|
||||
* @package SilverStripe\Forms\Schema
|
||||
*
|
||||
* Represents a {@link Form} as structured data which allows a frontend library to render it.
|
||||
* Includes information about the form as well as its fields.
|
||||
* Can create a "schema" (structure only) as well as "state" (data only).
|
||||
*/
|
||||
class FormSchema {
|
||||
|
||||
/**
|
||||
* Gets the schema for this form as a nested array.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
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 $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state of this form as a nested array.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
public function getState(Form $form) {
|
||||
$state = [
|
||||
'id' => $form->getName(),
|
||||
'fields' => [],
|
||||
'messages' => []
|
||||
];
|
||||
|
||||
foreach ($form->Fields()->dataFields() as $field) {
|
||||
$state['fields'][] = $field->getSchemaState();
|
||||
}
|
||||
|
||||
if($form->Message()) {
|
||||
$state['messages'][] = [
|
||||
'value' => $form->Message(),
|
||||
'type' => $form->MessageType(),
|
||||
];
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
@ -241,6 +241,74 @@ 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);
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
142
tests/forms/FormSchemaTest.php
Normal file
142
tests/forms/FormSchemaTest.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?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();
|
||||
$expected = [
|
||||
'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->assertInternalType('array', $schema);
|
||||
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($schema));
|
||||
}
|
||||
|
||||
public function testGetState() {
|
||||
$form = new Form(new Controller(), 'TestForm', new FieldList(), new FieldList());
|
||||
$formSchema = new FormSchema();
|
||||
$expected = [
|
||||
'id' => 'TestForm',
|
||||
'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' => []
|
||||
];
|
||||
|
||||
$state = $formSchema->getState($form);
|
||||
$this->assertInternalType('array', $state);
|
||||
$this->assertJsonStringEqualsJsonString(json_encode($expected), json_encode($state));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user