Standardise getCMSValidator for DataObjects/Forms

This commit is contained in:
cpenny 2020-02-13 12:51:56 +13:00
parent dd9b8ecd1f
commit d7dd93f7a7
7 changed files with 235 additions and 20 deletions

View File

@ -5,6 +5,7 @@ namespace SilverStripe\Forms;
use InvalidArgumentException; use InvalidArgumentException;
use SilverStripe\Control\RequestHandler; use SilverStripe\Control\RequestHandler;
use SilverStripe\Core\Extensible; use SilverStripe\Core\Extensible;
use SilverStripe\ORM\DataObject;
/** /**
* Default form builder class. * Default form builder class.
@ -96,15 +97,11 @@ class DefaultFormFactory implements FormFactory
*/ */
protected function getFormValidator(RequestHandler $controller = null, $name, $context = []) protected function getFormValidator(RequestHandler $controller = null, $name, $context = [])
{ {
$validator = null; if (!$context['Record'] instanceof DataObject) {
if ($context['Record']->hasMethod('getCMSValidator')) { return null;
// @todo Deprecate or formalise support for getCMSValidator()
$validator = $context['Record']->getCMSValidator();
} }
// Extend validator return $context['Record']->getCMSValidators();
$this->invokeWithExtensions('updateFormValidator', $validator, $controller, $name, $context);
return $validator;
} }
/** /**

View File

@ -1871,14 +1871,6 @@ class Form extends ViewableData implements HasRequestHandler
} }
// Don't cache if there are required fields, or some other complex validator // Don't cache if there are required fields, or some other complex validator
$validator = $this->getValidator(); return $this->getValidator()->canBeCached();
if ($validator instanceof RequiredFields) {
if (count($this->validator->getRequired())) {
return false;
}
} else {
return false;
}
return true;
} }
} }

View File

@ -121,10 +121,9 @@ class GridFieldDetailForm implements GridField_URLHandler
$gridField->getState(false)->setValue($gridStateStr); $gridField->getState(false)->setValue($gridStateStr);
} }
// if no validator has been set on the GridField and the record has a // if no validator has been set on the GridField then use the Validators from the record.
// CMS validator, use that. if (!$this->getValidator()) {
if (!$this->getValidator() && ClassInfo::hasMethod($record, 'getCMSValidator')) { $this->setValidator($record->getCMSValidators());
$this->setValidator($record->getCMSValidator());
} }
return $handler->handleRequest($request); return $handler->handleRequest($request);

View File

@ -215,4 +215,12 @@ class RequiredFields extends Validator
{ {
return array_values($this->required); return array_values($this->required);
} }
/**
* @return bool
*/
public function canBeCached(): bool
{
return count($this->getRequired()) === 0;
}
} }

View File

@ -168,6 +168,19 @@ abstract class Validator
return $this; return $this;
} }
/**
* When Validators are set on the form, it can affect whether or not the form cannot be cached. The base
* implementation always returns false (in order to match the current paradigm).
*
* @see RequiredFields for an example of when you might be able to cache your form.
*
* @return bool
*/
public function canBeCached(): bool
{
return false;
}
/** /**
* Clear current result * Clear current result
* *

192
src/Forms/ValidatorList.php Normal file
View File

@ -0,0 +1,192 @@
<?php
namespace SilverStripe\Forms;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\ValidationResult;
/**
* Class ValidatorList
*
* @package SilverStripe\Forms
*/
class ValidatorList extends Validator
{
/**
* @var ArrayList|Validator[]
*/
private $validators;
public function __construct()
{
$this->validators = ArrayList::create();
parent::__construct();
}
/**
* @param Form $form
* @return Validator
*/
public function setForm($form)
{
foreach ($this->getValidators() as $validator) {
$validator->setForm($form);
}
return parent::setForm($form);
}
/**
* Returns any errors there may be. This method considers the enabled status of the ValidatorList as a whole
* (exiting early if the List is disabled), as well as the enabled status of each individual Validator.
*
* @return ValidationResult
*/
public function validate()
{
$this->resetResult();
// This ValidatorList has been disabled in full
if (!$this->getEnabled()) {
return $this->result;
}
$data = $this->form->getData();
foreach ($this->getValidators() as $validator) {
// Reset the validation results for this Validator
$validator->resetResult();
// This Validator has been disabled, so skip it
if (!$validator->getEnabled()) {
continue;
}
// Run validation, and exit early if it's valid
if ($validator->php($data)) {
continue;
}
// Validation result was invalid. Combine our ValidationResult messages
$this->getResult()->combineAnd($validator->getResult());
}
return $this->result;
}
/**
* Returns whether the field in question is required. This will usually display '*' next to the
* field.
*
* @param string $fieldName
*
* @return bool
*/
public function fieldIsRequired($fieldName)
{
foreach ($this->getValidators() as $validator) {
if (!$validator->fieldIsRequired($fieldName)) {
continue;
}
return true;
}
return false;
}
/**
* Note: The existing implementations for the php() method (@see RequiredFields) does not check whether the
* Validator is enabled or not, and it also does not reset the validation result - so, neither does this.
*
* @param array $data
* @return bool
*/
public function php($data)
{
foreach ($this->getValidators() as $validator) {
// Run validation, and exit early if it's valid
if ($validator->php($data)) {
continue;
}
// Validation result was invalid. Combine our ValidationResult messages
$this->getResult()->combineAnd($validator->getResult());
}
// After collating results, return whether or not everything was valid
return $this->getResult()->isValid();
}
/**
* @param Validator $validator
* @return ValidatorList
*/
public function addValidator(Validator $validator): ValidatorList
{
$this->getValidators()->add($validator);
return $this;
}
/**
* @return ArrayList|Validator[]
*/
public function getValidators(): ArrayList
{
return $this->validators;
}
/**
* @param string $className
* @return ArrayList|Validator[]
*/
public function getValidatorsByType(string $className): ArrayList
{
$validators = ArrayList::create();
foreach ($this->getValidators() as $validator) {
if (!$validator instanceof $className) {
continue;
}
$validators->add($validator);
}
return $validators;
}
/**
* @param string $className
* @return ValidatorList
*/
public function removeByType(string $className): ValidatorList
{
foreach ($this->getValidators() as $validator) {
if (!$validator instanceof $className) {
continue;
}
$this->getValidators()->remove($validator);
}
return $this;
}
/**
* @return bool
*/
public function canBeCached(): bool
{
foreach ($this->getValidators() as $validator) {
if ($validator->canBeCached()) {
continue;
}
return false;
}
return true;
}
}

View File

@ -15,6 +15,7 @@ use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormField; use SilverStripe\Forms\FormField;
use SilverStripe\Forms\FormScaffolder; use SilverStripe\Forms\FormScaffolder;
use SilverStripe\Forms\ValidatorList;
use SilverStripe\i18n\i18n; use SilverStripe\i18n\i18n;
use SilverStripe\i18n\i18nEntityProvider; use SilverStripe\i18n\i18nEntityProvider;
use SilverStripe\ORM\Connect\MySQLSchemaManager; use SilverStripe\ORM\Connect\MySQLSchemaManager;
@ -2447,6 +2448,19 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
return $actions; return $actions;
} }
public function getCMSValidators(): ValidatorList
{
$validatorList = new ValidatorList();
// Support for the old method during the deprecation period
if ($this->hasMethod('getCMSValidator')) {
$validatorList->addValidator($this->getCMSValidator());
}
$this->invokeWithExtensions('updateCMSValidators', $validatorList);
return $validatorList;
}
/** /**
* Used for simple frontend forms without relation editing * Used for simple frontend forms without relation editing