silverstripe-framework/src/Forms/CompositeValidator.php

230 lines
6.4 KiB
PHP

<?php
namespace SilverStripe\Forms;
use InvalidArgumentException;
use SilverStripe\ORM\ValidationResult;
/**
* 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 getCMSCompositeValidator()
* method:
*
* public function getCMSCompositeValidator(): CompositeValidator
* {
* $compositeValidator = parent::getCMSCompositeValidator();
*
* $compositeValidator->addValidator(RequiredFields::create(['MyRequiredField']));
*
* return $compositeValidator
* }
*
* Or by implementing the updateCMSCompositeValidator() method in a DataExtension:
*
* public function updateCMSCompositeValidator(CompositeValidator $compositeValidator): void
* {
* $compositeValidator->addValidator(RequiredFields::create(['AdditionalContent']));
* }
*
* Class CompositeValidator
*
* @package SilverStripe\Forms
*/
class CompositeValidator extends Validator
{
/**
* @var array|Validator[]
*/
private $validators;
/**
* CompositeValidator constructor.
*
* @param array|Validator[] $validators
*/
public function __construct(array $validators = [])
{
$this->validators = array_values($validators ?? []);
parent::__construct();
}
/**
* Set the provided Form to the CompositeValidator and each Validator that has been added.
*
* @param Form $form
* @return Validator
*/
public function setForm($form)
{
foreach ($this->getValidators() as $validator) {
$validator->setForm($form);
}
return parent::setForm($form);
}
/**
* @param Validator $validator
* @return CompositeValidator
*/
public function addValidator(Validator $validator): CompositeValidator
{
$this->validators[] = $validator;
return $this;
}
/**
* 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.
*
* @return ValidationResult
*/
public function validate()
{
$this->resetResult();
// This CompositeValidator has been disabled in full
if (!$this->getEnabled()) {
return $this->result;
}
foreach ($this->getValidators() as $validator) {
// validate() will return a ValidationResult, and we will combine this with the result we already have
$this->getResult()->combineAnd($validator->validate());
}
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();
}
/**
* 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)) {
return true;
}
}
return false;
}
/**
* @return array|Validator[]
*/
public function getValidators(): array
{
return $this->validators;
}
/**
* Return all Validators that match a certain class name. EG: RequiredFields::class
*
* 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.
*
* @param string $className
* @return array|Validator[]
*/
public function getValidatorsByType(string $className): array
{
$validators = [];
foreach ($this->getValidators() as $key => $validator) {
if (!$validator instanceof $className) {
continue;
}
$validators[$key] = $validator;
}
return $validators;
}
/**
* Remove all Validators that match a certain class name. EG: RequiredFields::class
*
* @param string $className
* @return CompositeValidator
*/
public function removeValidatorsByType(string $className): CompositeValidator
{
foreach ($this->getValidatorsByType($className) as $key => $validator) {
$this->removeValidatorByKey($key);
}
return $this;
}
/**
* 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;
}
/**
* @internal This method may be updated to public in the future. Let us know if you feel there's a use case for it
* @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;
}
}