2020-02-13 00:51:56 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace SilverStripe\Forms;
|
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
use InvalidArgumentException;
|
2020-02-13 00:51:56 +01:00
|
|
|
use SilverStripe\ORM\ValidationResult;
|
|
|
|
|
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* 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.
|
|
|
|
*
|
2020-05-28 02:23:35 +02:00
|
|
|
* You can add Validators to the CompositeValidator in any DataObject by extending the getCMSCompositeValidator()
|
|
|
|
* method:
|
2020-05-27 23:35:07 +02:00
|
|
|
*
|
2020-05-28 02:23:35 +02:00
|
|
|
* public function getCMSCompositeValidator(): CompositeValidator
|
2020-05-27 23:35:07 +02:00
|
|
|
* {
|
2020-05-28 02:23:35 +02:00
|
|
|
* $compositeValidator = parent::getCMSCompositeValidator();
|
2020-05-27 23:35:07 +02:00
|
|
|
*
|
|
|
|
* $compositeValidator->addValidator(RequiredFields::create(['MyRequiredField']));
|
|
|
|
*
|
|
|
|
* return $compositeValidator
|
|
|
|
* }
|
|
|
|
*
|
2020-05-28 02:23:35 +02:00
|
|
|
* Or by implementing the updateCMSCompositeValidator() method in a DataExtension:
|
2020-05-27 23:35:07 +02:00
|
|
|
*
|
2020-05-28 02:23:35 +02:00
|
|
|
* public function updateCMSCompositeValidator(CompositeValidator $compositeValidator): void
|
2020-05-27 23:35:07 +02:00
|
|
|
* {
|
|
|
|
* $compositeValidator->addValidator(RequiredFields::create(['AdditionalContent']));
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* Class CompositeValidator
|
2020-02-13 00:51:56 +01:00
|
|
|
*
|
|
|
|
* @package SilverStripe\Forms
|
|
|
|
*/
|
2020-05-27 23:35:07 +02:00
|
|
|
class CompositeValidator extends Validator
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
|
|
|
/**
|
2020-02-19 20:54:30 +01:00
|
|
|
* @var array|Validator[]
|
2020-02-13 00:51:56 +01:00
|
|
|
*/
|
|
|
|
private $validators;
|
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* CompositeValidator constructor.
|
2020-02-19 20:54:30 +01:00
|
|
|
*
|
|
|
|
* @param array|Validator[] $validators
|
|
|
|
*/
|
|
|
|
public function __construct(array $validators = [])
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
2022-04-14 03:12:59 +02:00
|
|
|
$this->validators = array_values($validators ?? []);
|
2020-02-13 00:51:56 +01:00
|
|
|
|
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* Set the provided Form to the CompositeValidator and each Validator that has been added.
|
|
|
|
*
|
2020-02-13 00:51:56 +01:00
|
|
|
* @param Form $form
|
|
|
|
* @return Validator
|
|
|
|
*/
|
|
|
|
public function setForm($form)
|
|
|
|
{
|
|
|
|
foreach ($this->getValidators() as $validator) {
|
|
|
|
$validator->setForm($form);
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::setForm($form);
|
|
|
|
}
|
|
|
|
|
2020-02-13 20:21:22 +01:00
|
|
|
/**
|
|
|
|
* @param Validator $validator
|
2020-05-27 23:35:07 +02:00
|
|
|
* @return CompositeValidator
|
2020-02-13 20:21:22 +01:00
|
|
|
*/
|
2020-05-27 23:35:07 +02:00
|
|
|
public function addValidator(Validator $validator): CompositeValidator
|
2020-02-13 20:21:22 +01:00
|
|
|
{
|
2020-02-19 20:54:30 +01:00
|
|
|
$this->validators[] = $validator;
|
2020-02-13 20:21:22 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2020-02-13 00:51:56 +01:00
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* Returns any errors there may be. This method considers the enabled status of the CompositeValidator as a whole
|
|
|
|
* (exiting early if the Composite is disabled), as well as the enabled status of each individual Validator.
|
2020-02-13 00:51:56 +01:00
|
|
|
*
|
|
|
|
* @return ValidationResult
|
|
|
|
*/
|
|
|
|
public function validate()
|
|
|
|
{
|
|
|
|
$this->resetResult();
|
|
|
|
|
2020-05-27 23:35:07 +02:00
|
|
|
// This CompositeValidator has been disabled in full
|
2020-02-13 00:51:56 +01:00
|
|
|
if (!$this->getEnabled()) {
|
|
|
|
return $this->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->getValidators() as $validator) {
|
2021-02-21 20:13:54 +01:00
|
|
|
// validate() will return a ValidationResult, and we will combine this with the result we already have
|
|
|
|
$this->getResult()->combineAnd($validator->validate());
|
2020-02-13 00:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-13 20:21:22 +01:00
|
|
|
* Returns whether the field in question is required. This will usually display '*' next to the
|
|
|
|
* field.
|
|
|
|
*
|
|
|
|
* @param string $fieldName
|
|
|
|
*
|
|
|
|
* @return bool
|
2020-02-13 00:51:56 +01:00
|
|
|
*/
|
2020-02-13 20:21:22 +01:00
|
|
|
public function fieldIsRequired($fieldName)
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
2020-02-13 20:21:22 +01:00
|
|
|
foreach ($this->getValidators() as $validator) {
|
|
|
|
if ($validator->fieldIsRequired($fieldName)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2020-02-13 00:51:56 +01:00
|
|
|
|
2020-02-13 20:21:22 +01:00
|
|
|
return false;
|
2020-02-13 00:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-19 20:54:30 +01:00
|
|
|
* @return array|Validator[]
|
2020-02-13 00:51:56 +01:00
|
|
|
*/
|
2020-02-19 20:54:30 +01:00
|
|
|
public function getValidators(): array
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
|
|
|
return $this->validators;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* Return all Validators that match a certain class name. EG: RequiredFields::class
|
|
|
|
*
|
2021-03-10 10:59:06 +01:00
|
|
|
* The keys for the return array match the keys in the unfiltered array. You cannot assume the keys will be
|
|
|
|
* sequential or that the first key will be ZERO.
|
|
|
|
*
|
2020-02-13 00:51:56 +01:00
|
|
|
* @param string $className
|
2020-02-19 20:54:30 +01:00
|
|
|
* @return array|Validator[]
|
2020-02-13 00:51:56 +01:00
|
|
|
*/
|
2020-02-19 20:54:30 +01:00
|
|
|
public function getValidatorsByType(string $className): array
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
2020-02-19 20:54:30 +01:00
|
|
|
$validators = [];
|
2020-02-13 00:51:56 +01:00
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
foreach ($this->getValidators() as $key => $validator) {
|
2020-02-13 00:51:56 +01:00
|
|
|
if (!$validator instanceof $className) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
$validators[$key] = $validator;
|
2020-02-13 00:51:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $validators;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 23:35:07 +02:00
|
|
|
* Remove all Validators that match a certain class name. EG: RequiredFields::class
|
|
|
|
*
|
2020-02-13 00:51:56 +01:00
|
|
|
* @param string $className
|
2020-05-27 23:35:07 +02:00
|
|
|
* @return CompositeValidator
|
2020-02-13 00:51:56 +01:00
|
|
|
*/
|
2020-05-27 23:35:07 +02:00
|
|
|
public function removeValidatorsByType(string $className): CompositeValidator
|
2020-02-13 00:51:56 +01:00
|
|
|
{
|
2020-02-19 20:54:30 +01:00
|
|
|
foreach ($this->getValidatorsByType($className) as $key => $validator) {
|
|
|
|
$this->removeValidatorByKey($key);
|
|
|
|
}
|
2020-02-13 00:51:56 +01:00
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2020-05-27 23:35:07 +02:00
|
|
|
/**
|
|
|
|
* Each Validator is aware of whether or not it can be cached. If even one Validator cannot be cached, then the
|
|
|
|
* CompositeValidator as a whole also cannot be cached.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function canBeCached(): bool
|
|
|
|
{
|
|
|
|
foreach ($this->getValidators() as $validator) {
|
|
|
|
if (!$validator->canBeCached()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
/**
|
2020-05-28 02:23:35 +02:00
|
|
|
* @internal This method may be updated to public in the future. Let us know if you feel there's a use case for it
|
2020-02-19 20:54:30 +01:00
|
|
|
* @param int $key
|
2020-05-27 23:35:07 +02:00
|
|
|
* @return CompositeValidator
|
2020-02-19 20:54:30 +01:00
|
|
|
*/
|
2020-05-27 23:35:07 +02:00
|
|
|
protected function removeValidatorByKey(int $key): CompositeValidator
|
2020-02-19 20:54:30 +01:00
|
|
|
{
|
2022-04-14 03:12:59 +02:00
|
|
|
if (!array_key_exists($key, $this->validators ?? [])) {
|
2020-02-19 20:54:30 +01:00
|
|
|
throw new InvalidArgumentException(
|
|
|
|
sprintf('Key "%s" does not exist in $validators array', $key)
|
|
|
|
);
|
2020-02-13 00:51:56 +01:00
|
|
|
}
|
|
|
|
|
2020-02-19 20:54:30 +01:00
|
|
|
unset($this->validators[$key]);
|
|
|
|
|
2020-02-13 00:51:56 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
}
|