Update name to CompositeValidator. Add docblocks

This commit is contained in:
cpenny 2020-05-28 09:35:07 +12:00
parent a2b57f0801
commit bca4be77ed
8 changed files with 128 additions and 94 deletions

View File

@ -275,7 +275,7 @@ class MyController extends Controller
In the CMS, we're not creating the forms for editing CMS records. The `Form` instance is generated for us so we cannot In the CMS, we're not creating the forms for editing CMS records. The `Form` instance is generated for us so we cannot
call `setValidator` easily. However, a `DataObject` can provide its' own `Validator` instance/s through the call `setValidator` easily. However, a `DataObject` can provide its' own `Validator` instance/s through the
`getValidatorList()` method. The CMS interfaces such as [LeftAndMain](api:SilverStripe\Admin\LeftAndMain), `getCompositeValidator()` method. The CMS interfaces such as [LeftAndMain](api:SilverStripe\Admin\LeftAndMain),
[ModelAdmin](api:SilverStripe\Admin\ModelAdmin) and [GridField](api:SilverStripe\Forms\GridField\GridField) will [ModelAdmin](api:SilverStripe\Admin\ModelAdmin) and [GridField](api:SilverStripe\Forms\GridField\GridField) will
respect the provided `Validator`/s and handle displaying error and success responses to the user. respect the provided `Validator`/s and handle displaying error and success responses to the user.

View File

@ -6,11 +6,35 @@ use InvalidArgumentException;
use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\ValidationResult;
/** /**
* Class ValidatorList * CompositeValidator can contain between 0 and many different types of Validators. Each Validator is itself still
* responsible for Validating its form and generating its ValidationResult.
*
* Once each Validator has generated its ValidationResult, the CompositeValidator will combine these results into a
* single ValidationResult. This single ValidationResult is what will be returned by the CompositeValidator.
*
* You can add Validators to the CompositeValidator in any DataObject by extending the getCompositeValidator() method:
*
* public function getCompositeValidator(): CompositeValidator
* {
* $compositeValidator = parent::getCompositeValidator();
*
* $compositeValidator->addValidator(RequiredFields::create(['MyRequiredField']));
*
* return $compositeValidator
* }
*
* Or by implementing the updateCompositeValidator() method in a DataExtension:
*
* public function updateCompositeValidator(CompositeValidator $compositeValidator): void
* {
* $compositeValidator->addValidator(RequiredFields::create(['AdditionalContent']));
* }
*
* Class CompositeValidator
* *
* @package SilverStripe\Forms * @package SilverStripe\Forms
*/ */
class ValidatorList extends Validator class CompositeValidator extends Validator
{ {
/** /**
* @var array|Validator[] * @var array|Validator[]
@ -18,7 +42,7 @@ class ValidatorList extends Validator
private $validators; private $validators;
/** /**
* ValidatorList constructor. * CompositeValidator constructor.
* *
* @param array|Validator[] $validators * @param array|Validator[] $validators
*/ */
@ -30,6 +54,8 @@ class ValidatorList extends Validator
} }
/** /**
* Set the provided Form to the CompositeValidator and each Validator that has been added.
*
* @param Form $form * @param Form $form
* @return Validator * @return Validator
*/ */
@ -44,9 +70,9 @@ class ValidatorList extends Validator
/** /**
* @param Validator $validator * @param Validator $validator
* @return ValidatorList * @return CompositeValidator
*/ */
public function addValidator(Validator $validator): ValidatorList public function addValidator(Validator $validator): CompositeValidator
{ {
$this->validators[] = $validator; $this->validators[] = $validator;
@ -54,8 +80,8 @@ class ValidatorList extends Validator
} }
/** /**
* Returns any errors there may be. This method considers the enabled status of the ValidatorList as a whole * Returns any errors there may be. This method considers the enabled status of the CompositeValidator as a whole
* (exiting early if the List is disabled), as well as the enabled status of each individual Validator. * (exiting early if the Composite is disabled), as well as the enabled status of each individual Validator.
* *
* @return ValidationResult * @return ValidationResult
*/ */
@ -63,7 +89,7 @@ class ValidatorList extends Validator
{ {
$this->resetResult(); $this->resetResult();
// This ValidatorList has been disabled in full // This CompositeValidator has been disabled in full
if (!$this->getEnabled()) { if (!$this->getEnabled()) {
return $this->result; return $this->result;
} }
@ -142,6 +168,8 @@ class ValidatorList extends Validator
} }
/** /**
* Return all Validators that match a certain class name. EG: RequiredFields::class
*
* @param string $className * @param string $className
* @return array|Validator[] * @return array|Validator[]
*/ */
@ -161,10 +189,12 @@ class ValidatorList extends Validator
} }
/** /**
* Remove all Validators that match a certain class name. EG: RequiredFields::class
*
* @param string $className * @param string $className
* @return ValidatorList * @return CompositeValidator
*/ */
public function removeValidatorsByType(string $className): ValidatorList public function removeValidatorsByType(string $className): CompositeValidator
{ {
foreach ($this->getValidatorsByType($className) as $key => $validator) { foreach ($this->getValidatorsByType($className) as $key => $validator) {
$this->removeValidatorByKey($key); $this->removeValidatorByKey($key);
@ -174,23 +204,9 @@ class ValidatorList extends Validator
} }
/** /**
* @param int $key * Each Validator is aware of whether or not it can be cached. If even one Validator cannot be cached, then the
* @return ValidatorList * CompositeValidator as a whole also cannot be cached.
*/ *
public function removeValidatorByKey(int $key): ValidatorList
{
if (!array_key_exists($key, $this->validators)) {
throw new InvalidArgumentException(
sprintf('Key "%s" does not exist in $validators array', $key)
);
}
unset($this->validators[$key]);
return $this;
}
/**
* @return bool * @return bool
*/ */
public function canBeCached(): bool public function canBeCached(): bool
@ -203,4 +219,21 @@ class ValidatorList extends Validator
return true; return true;
} }
/**
* @param int $key
* @return CompositeValidator
*/
protected function removeValidatorByKey(int $key): CompositeValidator
{
if (!array_key_exists($key, $this->validators)) {
throw new InvalidArgumentException(
sprintf('Key "%s" does not exist in $validators array', $key)
);
}
unset($this->validators[$key]);
return $this;
}
} }

View File

@ -101,17 +101,17 @@ class DefaultFormFactory implements FormFactory
return null; return null;
} }
$validatorList = $context['Record']->getValidatorList(); $compositeValidator = $context['Record']->getCompositeValidator();
// Extend validator - legacy support, will be removed in 5.0.0 // Extend validator - legacy support, will be removed in 5.0.0
foreach ($validatorList->getValidators() as $validator) { foreach ($compositeValidator->getValidators() as $validator) {
$this->invokeWithExtensions('updateFormValidator', $validator, $controller, $name, $context); $this->invokeWithExtensions('updateFormValidator', $validator, $controller, $name, $context);
} }
// Extend validator - forward support, will be supported beyond 5.0.0 // Extend validator - forward support, will be supported beyond 5.0.0
$this->invokeWithExtensions('updateValidatorList', $validatorList); $this->invokeWithExtensions('updateCompositeValidator', $compositeValidator);
return $validatorList; return $compositeValidator;
} }
/** /**

View File

@ -123,7 +123,7 @@ class GridFieldDetailForm implements GridField_URLHandler
// if no validator has been set on the GridField then use the Validators from the record. // if no validator has been set on the GridField then use the Validators from the record.
if (!$this->getValidator()) { if (!$this->getValidator()) {
$this->setValidator($record->getValidatorList()); $this->setValidator($record->getCompositeValidator());
} }
return $handler->handleRequest($request); return $handler->handleRequest($request);

View File

@ -5,7 +5,7 @@ namespace SilverStripe\ORM;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Extension; use SilverStripe\Core\Extension;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\ValidatorList; use SilverStripe\Forms\CompositeValidator;
use SilverStripe\ORM\Queries\SQLSelect; use SilverStripe\ORM\Queries\SQLSelect;
use Exception; use Exception;
@ -137,11 +137,11 @@ abstract class DataExtension extends Extension
/** /**
* This function is used to provide modifications to the Validators used on a DataObject. * This function is used to provide modifications to the Validators used on a DataObject.
* *
* Caution: Use {@link ValidatorList->addValidator()} to add Validators. * Caution: Use {@link CompositeValidator->addValidator()} to add Validators.
* *
* @param ValidatorList $validatorList * @param CompositeValidator $compositeValidator
*/ */
public function updateValidatorList(ValidatorList $validatorList): void public function updateCompositeValidator(CompositeValidator $compositeValidator): void
{ {
} }

View File

@ -15,7 +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\Forms\CompositeValidator;
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;
@ -2449,25 +2449,26 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} }
/** /**
* @return ValidatorList * @return CompositeValidator
*/ */
public function getValidatorList(): ValidatorList public function getCompositeValidator(): CompositeValidator
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
// Support for the old method during the deprecation period // Support for the old method during the deprecation period
if ($this->hasMethod('getCMSValidator')) { if ($this->hasMethod('getCMSValidator')) {
Deprecation::notice( Deprecation::notice(
'4.6', '4.6',
'getCMSValidator() is removed in 5.0 in favour of getValidatorList()' 'getCMSValidator() is removed in 5.0 in favour of getCompositeValidator()'
); );
$validatorList->addValidator($this->getCMSValidator()); $compositeValidator->addValidator($this->getCMSValidator());
} }
$this->invokeWithExtensions('updateValidatorList', $validatorList); // Extend validator - forward support, will be supported beyond 5.0.0
$this->invokeWithExtensions('updateCompositeValidator', $compositeValidator);
return $validatorList; return $compositeValidator;
} }
/** /**

View File

@ -8,15 +8,15 @@ use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form; use SilverStripe\Forms\Form;
use SilverStripe\Forms\RequiredFields; use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\Tests\ValidatorTest\TestValidator; use SilverStripe\Forms\Tests\ValidatorTest\TestValidator;
use SilverStripe\Forms\Tests\ValidatorTest\TestValidatorList; use SilverStripe\Forms\Tests\ValidatorTest\TestCompositeValidator;
use SilverStripe\Forms\TextField; use SilverStripe\Forms\TextField;
use SilverStripe\Forms\ValidatorList; use SilverStripe\Forms\CompositeValidator;
/** /**
* @package framework * @package framework
* @subpackage tests * @subpackage tests
*/ */
class ValidatorListTest extends SapphireTest class CompositeValidatorTest extends SapphireTest
{ {
/** /**
* Common method for setting up form, since that will always be a dependency for the validator. * Common method for setting up form, since that will always be a dependency for the validator.
@ -38,28 +38,28 @@ class ValidatorListTest extends SapphireTest
public function testAddValidator(): void public function testAddValidator(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$this->assertCount(2, $validatorList->getValidators()); $this->assertCount(2, $compositeValidator->getValidators());
} }
public function testSetForm(): void public function testSetForm(): void
{ {
$form = $this->getForm(); $form = $this->getForm();
$validatorList = new TestValidatorList(); $compositeValidator = new TestCompositeValidator();
$validator = new TestValidator(); $validator = new TestValidator();
$validatorList->addValidator($validator); $compositeValidator->addValidator($validator);
$validatorList->setForm($form); $compositeValidator->setForm($form);
$this->assertNotNull($validatorList->getForm()); $this->assertNotNull($compositeValidator->getForm());
$this->assertCount(1, $validatorList->getValidators()); $this->assertCount(1, $compositeValidator->getValidators());
foreach ($validatorList->getValidators() as $validator) { foreach ($compositeValidator->getValidators() as $validator) {
/** @var TestValidator $validator */ /** @var TestValidator $validator */
$this->assertNotNull($validator->getForm()); $this->assertNotNull($validator->getForm());
} }
@ -67,46 +67,46 @@ class ValidatorListTest extends SapphireTest
public function testGetValidatorsByType(): void public function testGetValidatorsByType(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$validatorList->addValidator(new TestValidator()); $compositeValidator->addValidator(new TestValidator());
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$validatorList->addValidator(new TestValidator()); $compositeValidator->addValidator(new TestValidator());
$this->assertCount(4, $validatorList->getValidators()); $this->assertCount(4, $compositeValidator->getValidators());
$this->assertCount(2, $validatorList->getValidatorsByType(RequiredFields::class)); $this->assertCount(2, $compositeValidator->getValidatorsByType(RequiredFields::class));
} }
public function testRemoveValidatorsByType(): void public function testRemoveValidatorsByType(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$validatorList->addValidator(new TestValidator()); $compositeValidator->addValidator(new TestValidator());
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$validatorList->addValidator(new TestValidator()); $compositeValidator->addValidator(new TestValidator());
$this->assertCount(4, $validatorList->getValidators()); $this->assertCount(4, $compositeValidator->getValidators());
$validatorList->removeValidatorsByType(RequiredFields::class); $compositeValidator->removeValidatorsByType(RequiredFields::class);
$this->assertCount(2, $validatorList->getValidators()); $this->assertCount(2, $compositeValidator->getValidators());
} }
public function testCanBeCached(): void public function testCanBeCached(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new RequiredFields()); $compositeValidator->addValidator(new RequiredFields());
$this->assertTrue($validatorList->canBeCached()); $this->assertTrue($compositeValidator->canBeCached());
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new RequiredFields(['Foor'])); $compositeValidator->addValidator(new RequiredFields(['Foor']));
$this->assertFalse($validatorList->canBeCached()); $this->assertFalse($compositeValidator->canBeCached());
} }
public function testFieldIsRequired(): void public function testFieldIsRequired(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$fieldNames = [ $fieldNames = [
'Title', 'Title',
@ -124,12 +124,12 @@ class ValidatorListTest extends SapphireTest
] ]
); );
$validatorList->addValidator($requiredFieldsFirst); $compositeValidator->addValidator($requiredFieldsFirst);
$validatorList->addValidator($requiredFieldsSecond); $compositeValidator->addValidator($requiredFieldsSecond);
foreach ($fieldNames as $field) { foreach ($fieldNames as $field) {
$this->assertTrue( $this->assertTrue(
$validatorList->fieldIsRequired($field), $compositeValidator->fieldIsRequired($field),
sprintf('Failed to find "%s" field in required list', $field) sprintf('Failed to find "%s" field in required list', $field)
); );
} }
@ -137,10 +137,10 @@ class ValidatorListTest extends SapphireTest
public function testValidate(): void public function testValidate(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
// Add two separate validators, each with one required field // Add two separate validators, each with one required field
$validatorList->addValidator(new RequiredFields(['Foo'])); $compositeValidator->addValidator(new RequiredFields(['Foo']));
$validatorList->addValidator(new RequiredFields(['Bar'])); $compositeValidator->addValidator(new RequiredFields(['Bar']));
// Setup a form with the fields/data we're testing (a form is a dependency for validation right now) // Setup a form with the fields/data we're testing (a form is a dependency for validation right now)
// We'll add three empty fields, but only two of them should be required // We'll add three empty fields, but only two of them should be required
@ -153,7 +153,7 @@ class ValidatorListTest extends SapphireTest
$form = $this->getForm(array_keys($data)); $form = $this->getForm(array_keys($data));
$form->disableSecurityToken(); $form->disableSecurityToken();
// Setup validator now that we've got our form // Setup validator now that we've got our form
$form->setValidator($validatorList); $form->setValidator($compositeValidator);
// Put data into the form so the validator can pull it back out again // Put data into the form so the validator can pull it back out again
$form->loadDataFrom($data); $form->loadDataFrom($data);
@ -164,8 +164,8 @@ class ValidatorListTest extends SapphireTest
public function testRemoveValidation(): void public function testRemoveValidation(): void
{ {
$validatorList = new ValidatorList(); $compositeValidator = new CompositeValidator();
$validatorList->addValidator(new TestValidator()); $compositeValidator->addValidator(new TestValidator());
// Setup a form with the fields/data we're testing (a form is a dependency for validation right now) // Setup a form with the fields/data we're testing (a form is a dependency for validation right now)
$data = [ $data = [
@ -175,7 +175,7 @@ class ValidatorListTest extends SapphireTest
$form = $this->getForm(array_keys($data)); $form = $this->getForm(array_keys($data));
$form->disableSecurityToken(); $form->disableSecurityToken();
// Setup validator now that we've got our form // Setup validator now that we've got our form
$form->setValidator($validatorList); $form->setValidator($compositeValidator);
// Put data into the form so the validator can pull it back out again // Put data into the form so the validator can pull it back out again
$form->loadDataFrom($data); $form->loadDataFrom($data);
@ -185,7 +185,7 @@ class ValidatorListTest extends SapphireTest
// Make sure it doesn't fail after removing validation AND has no errors (since calling validate should reset // Make sure it doesn't fail after removing validation AND has no errors (since calling validate should reset
// errors) // errors)
$validatorList->removeValidation(); $compositeValidator->removeValidation();
$result = $form->validationResult(); $result = $form->validationResult();
$this->assertTrue($result->isValid()); $this->assertTrue($result->isValid());
$this->assertEmpty($result->getMessages()); $this->assertEmpty($result->getMessages());

View File

@ -4,14 +4,14 @@ namespace SilverStripe\Forms\Tests\ValidatorTest;
use SilverStripe\Dev\TestOnly; use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\Form; use SilverStripe\Forms\Form;
use SilverStripe\Forms\ValidatorList; use SilverStripe\Forms\CompositeValidator;
/** /**
* Class TestValidatorList * Class TestCompositeValidator
* *
* @package SilverStripe\Forms\Tests\ValidatorTest * @package SilverStripe\Forms\Tests\ValidatorTest
*/ */
class TestValidatorList extends ValidatorList implements TestOnly class TestCompositeValidator extends CompositeValidator implements TestOnly
{ {
/** /**
* Allow us to access the form for test purposes. * Allow us to access the form for test purposes.