mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW Validate DBFields
This commit is contained in:
parent
c523022cb9
commit
e10f6f421a
@ -20,6 +20,8 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
class: SilverStripe\ORM\FieldType\DBDecimal
|
class: SilverStripe\ORM\FieldType\DBDecimal
|
||||||
Double:
|
Double:
|
||||||
class: SilverStripe\ORM\FieldType\DBDouble
|
class: SilverStripe\ORM\FieldType\DBDouble
|
||||||
|
Email:
|
||||||
|
class: SilverStripe\ORM\FieldType\DBEmail
|
||||||
Enum:
|
Enum:
|
||||||
class: SilverStripe\ORM\FieldType\DBEnum
|
class: SilverStripe\ORM\FieldType\DBEnum
|
||||||
Float:
|
Float:
|
||||||
|
@ -2,11 +2,18 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Forms;
|
namespace SilverStripe\Forms;
|
||||||
|
|
||||||
|
use SilverStripe\Validation\EmailValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text input field with validation for correct email format according to RFC 2822.
|
* Text input field with validation for correct email format
|
||||||
*/
|
*/
|
||||||
class EmailField extends TextField
|
class EmailField extends TextField
|
||||||
{
|
{
|
||||||
|
private static array $field_validators = [
|
||||||
|
[
|
||||||
|
'class' => EmailValidator::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
protected $inputType = 'email';
|
protected $inputType = 'email';
|
||||||
/**
|
/**
|
||||||
@ -17,39 +24,6 @@ class EmailField extends TextField
|
|||||||
return 'email text';
|
return 'email text';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates for RFC 2822 compliant email addresses.
|
|
||||||
*
|
|
||||||
* @see http://www.regular-expressions.info/email.html
|
|
||||||
* @see http://www.ietf.org/rfc/rfc2822.txt
|
|
||||||
*
|
|
||||||
* @param Validator $validator
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function validate($validator)
|
|
||||||
{
|
|
||||||
$result = true;
|
|
||||||
$this->value = trim($this->value ?? '');
|
|
||||||
|
|
||||||
$pattern = '^[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$';
|
|
||||||
|
|
||||||
// Escape delimiter characters.
|
|
||||||
$safePattern = str_replace('/', '\\/', $pattern ?? '');
|
|
||||||
|
|
||||||
if ($this->value && !preg_match('/' . $safePattern . '/i', $this->value ?? '')) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t('SilverStripe\\Forms\\EmailField.VALIDATION', 'Please enter an email address'),
|
|
||||||
'validation'
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->extendValidationResult($result, $validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSchemaValidation()
|
public function getSchemaValidation()
|
||||||
{
|
{
|
||||||
$rules = parent::getSchemaValidation();
|
$rules = parent::getSchemaValidation();
|
||||||
|
@ -15,6 +15,8 @@ use SilverStripe\Core\Validation\ValidationResult;
|
|||||||
use SilverStripe\View\AttributesHTML;
|
use SilverStripe\View\AttributesHTML;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use SilverStripe\Model\ModelData;
|
use SilverStripe\Model\ModelData;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Validation\FieldValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a field in a form.
|
* Represents a field in a form.
|
||||||
@ -275,6 +277,8 @@ class FormField extends RequestHandler
|
|||||||
'Description' => 'HTMLFragment',
|
'Description' => 'HTMLFragment',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static array $field_validators = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structured schema state representing the FormField's current data and validation.
|
* Structured schema state representing the FormField's current data and validation.
|
||||||
* Used to render the FormField as a ReactJS Component on the front-end.
|
* Used to render the FormField as a ReactJS Component on the front-end.
|
||||||
@ -1231,15 +1235,25 @@ class FormField extends RequestHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method each {@link FormField} subclass must implement, determines whether the field
|
* Subclasses can define an existing FieldValidatorClass to validate the FormField value
|
||||||
* is valid or not based on the value.
|
* They may also override this method to provide custom validation logic
|
||||||
*
|
*
|
||||||
* @param Validator $validator
|
* @param Validator $validator
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function validate($validator)
|
public function validate($validator)
|
||||||
{
|
{
|
||||||
return $this->extendValidationResult(true, $validator);
|
$isValid = true;
|
||||||
|
$name = strip_tags($this->Title() ? $this->Title() : $this->getName());
|
||||||
|
$fieldValidators = FieldValidator::createFieldValidatorsForField($this, $name, $this->value);
|
||||||
|
foreach ($fieldValidators as $fieldValidator) {
|
||||||
|
$validationResult = $fieldValidator->validate();
|
||||||
|
if (!$validationResult->isValid()) {
|
||||||
|
$validator->getResult()->combineAnd($validationResult);
|
||||||
|
$isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->extendValidationResult($isValid, $validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Forms;
|
namespace SilverStripe\Forms;
|
||||||
|
|
||||||
|
use SilverStripe\Validation\StringLengthValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text input field.
|
* Text input field.
|
||||||
*/
|
*/
|
||||||
@ -14,6 +16,13 @@ class TextField extends FormField implements TippableFieldInterface
|
|||||||
|
|
||||||
protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_TEXT;
|
protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_TEXT;
|
||||||
|
|
||||||
|
private static array $field_validators = [
|
||||||
|
[
|
||||||
|
'class' => StringLengthValidator::class,
|
||||||
|
'argCalls' => [null, 'getMaxLength'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Tip|null A tip to render beside the input
|
* @var Tip|null A tip to render beside the input
|
||||||
*/
|
*/
|
||||||
@ -117,31 +126,6 @@ class TextField extends FormField implements TippableFieldInterface
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this field
|
|
||||||
*
|
|
||||||
* @param Validator $validator
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function validate($validator)
|
|
||||||
{
|
|
||||||
$result = true;
|
|
||||||
if (!is_null($this->maxLength) && mb_strlen($this->value ?? '') > $this->maxLength) {
|
|
||||||
$name = strip_tags($this->Title() ? $this->Title() : $this->getName());
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'SilverStripe\\Forms\\TextField.VALIDATEMAXLENGTH',
|
|
||||||
'The value for {name} must not exceed {maxLength} characters in length',
|
|
||||||
['name' => $name, 'maxLength' => $this->maxLength]
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
$result = false;
|
|
||||||
}
|
|
||||||
return $this->extendValidationResult($result, $validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSchemaValidation()
|
public function getSchemaValidation()
|
||||||
{
|
{
|
||||||
$rules = parent::getSchemaValidation();
|
$rules = parent::getSchemaValidation();
|
||||||
|
@ -1230,6 +1230,15 @@ class DataObject extends ModelData implements DataObjectInterface, i18nEntityPro
|
|||||||
public function validate()
|
public function validate()
|
||||||
{
|
{
|
||||||
$result = ValidationResult::create();
|
$result = ValidationResult::create();
|
||||||
|
// Call DBField::validate() on every DBField
|
||||||
|
$specs = static::getSchema()->fieldSpecs(static::class);
|
||||||
|
foreach (array_keys($specs) as $fieldName) {
|
||||||
|
$dbField = $this->dbObject($fieldName);
|
||||||
|
$validationResult = $dbField->validate();
|
||||||
|
if (!$validationResult->isValid()) {
|
||||||
|
$result->combineAnd($validationResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->extend('updateValidate', $result);
|
$this->extend('updateValidate', $result);
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
31
src/ORM/FieldType/DBEmail.php
Normal file
31
src/ORM/FieldType/DBEmail.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM\FieldType;
|
||||||
|
|
||||||
|
use SilverStripe\Forms\EmailField;
|
||||||
|
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||||
|
use SilverStripe\Validation\EmailValidator;
|
||||||
|
use SilverStripe\Forms\FormField;
|
||||||
|
use SilverStripe\Forms\NullableField;
|
||||||
|
|
||||||
|
class DBEmail extends DBVarchar
|
||||||
|
{
|
||||||
|
private static array $field_validators = [
|
||||||
|
[
|
||||||
|
'class' => EmailValidator::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
public function scaffoldFormField(?string $title = null, array $params = []): ?FormField
|
||||||
|
{
|
||||||
|
// Set field with appropriate size
|
||||||
|
$field = EmailField::create($this->name, $title);
|
||||||
|
$field->setMaxLength($this->getSize());
|
||||||
|
|
||||||
|
// Allow the user to select if it's null instead of automatically assuming empty string is
|
||||||
|
if (!$this->getNullifyEmpty()) {
|
||||||
|
return NullableField::create($field);
|
||||||
|
}
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,8 @@ use SilverStripe\Forms\TextField;
|
|||||||
use SilverStripe\ORM\Filters\SearchFilter;
|
use SilverStripe\ORM\Filters\SearchFilter;
|
||||||
use SilverStripe\ORM\Queries\SQLSelect;
|
use SilverStripe\ORM\Queries\SQLSelect;
|
||||||
use SilverStripe\Model\ModelData;
|
use SilverStripe\Model\ModelData;
|
||||||
|
use SilverStripe\Core\Validation\ValidationResult;
|
||||||
|
use SilverStripe\Validation\FieldValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single field in the database.
|
* Single field in the database.
|
||||||
@ -43,7 +45,6 @@ use SilverStripe\Model\ModelData;
|
|||||||
*/
|
*/
|
||||||
abstract class DBField extends ModelData implements DBIndexable
|
abstract class DBField extends ModelData implements DBIndexable
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw value of this field
|
* Raw value of this field
|
||||||
*/
|
*/
|
||||||
@ -99,6 +100,8 @@ abstract class DBField extends ModelData implements DBIndexable
|
|||||||
'ProcessedRAW' => 'HTMLFragment',
|
'ProcessedRAW' => 'HTMLFragment',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static array $field_validators = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default value in the database.
|
* Default value in the database.
|
||||||
* Might be overridden on DataObject-level, but still useful for setting defaults on
|
* Might be overridden on DataObject-level, but still useful for setting defaults on
|
||||||
@ -468,6 +471,22 @@ abstract class DBField extends ModelData implements DBIndexable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate this field. Called during DataObject::validate().
|
||||||
|
*/
|
||||||
|
public function validate(): ValidationResult
|
||||||
|
{
|
||||||
|
$result = ValidationResult::create();
|
||||||
|
$fieldValidators = FieldValidator::createFieldValidatorsForField($this, $this->getName(), $this->getValue());
|
||||||
|
foreach ($fieldValidators as $fieldValidator) {
|
||||||
|
$validationResult = $fieldValidator->validate();
|
||||||
|
if (!$validationResult->isValid()) {
|
||||||
|
$result->combineAnd($validationResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a FormField instance used as a default
|
* Returns a FormField instance used as a default
|
||||||
* for form scaffolding.
|
* for form scaffolding.
|
||||||
|
@ -8,6 +8,7 @@ use SilverStripe\Forms\NullableField;
|
|||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\ORM\Connect\MySQLDatabase;
|
use SilverStripe\ORM\Connect\MySQLDatabase;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\Validation\StringLengthValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Varchar represents a variable-length string of up to 255 characters, designed to store raw text
|
* Class Varchar represents a variable-length string of up to 255 characters, designed to store raw text
|
||||||
@ -18,6 +19,13 @@ use SilverStripe\ORM\DB;
|
|||||||
*/
|
*/
|
||||||
class DBVarchar extends DBString
|
class DBVarchar extends DBString
|
||||||
{
|
{
|
||||||
|
private static array $field_validators = [
|
||||||
|
[
|
||||||
|
'class' => StringLengthValidator::class,
|
||||||
|
'argCalls' => [null, 'getSize'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
private static array $casting = [
|
private static array $casting = [
|
||||||
'Initial' => 'Text',
|
'Initial' => 'Text',
|
||||||
'URL' => 'Text',
|
'URL' => 'Text',
|
||||||
|
24
src/Validation/EmailValidator.php
Normal file
24
src/Validation/EmailValidator.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Validation;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Validation\ValidationResult;
|
||||||
|
use SilverStripe\Validation\FieldValidator;
|
||||||
|
use SilverStripe\Core\Validation\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Constraints;
|
||||||
|
use SilverStripe\Forms\FormField;
|
||||||
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
|
|
||||||
|
class EmailValidator extends FieldValidator
|
||||||
|
{
|
||||||
|
protected function validateValue(ValidationResult $result): ValidationResult
|
||||||
|
{
|
||||||
|
$message = _t('SilverStripe\\Forms\\EmailField.VALIDATION', 'Please enter an email address');
|
||||||
|
$validationResult = ConstraintValidator::validate(
|
||||||
|
$this->value,
|
||||||
|
new Constraints\Email(message: $message),
|
||||||
|
$this->name
|
||||||
|
);
|
||||||
|
return $result->combineAnd($validationResult);
|
||||||
|
}
|
||||||
|
}
|
67
src/Validation/FieldValidator.php
Normal file
67
src/Validation/FieldValidator.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Validation;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use SilverStripe\Core\Validation\ValidationResult;
|
||||||
|
use SilverStripe\Forms\FormField;
|
||||||
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class that can be used as a validator for FormFields and DBFields
|
||||||
|
*/
|
||||||
|
abstract class FieldValidator
|
||||||
|
{
|
||||||
|
protected string $name;
|
||||||
|
protected mixed $value;
|
||||||
|
|
||||||
|
public function __construct(string $name, mixed $value)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(): ValidationResult
|
||||||
|
{
|
||||||
|
$result = ValidationResult::create();
|
||||||
|
$result = $this->validateValue($result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function validateValue(ValidationResult $result): ValidationResult;
|
||||||
|
|
||||||
|
public static function createFieldValidatorsForField(
|
||||||
|
FormField|DBField $field,
|
||||||
|
string $name,
|
||||||
|
mixed $value
|
||||||
|
): array {
|
||||||
|
$fieldValidators = [];
|
||||||
|
$config = $field->config()->get('field_validators');
|
||||||
|
foreach ($config as $spec) {
|
||||||
|
$class = $spec['class'];
|
||||||
|
$argCalls = $spec['argCalls'] ?? null;
|
||||||
|
if (!is_a($class, FieldValidator::class, true)) {
|
||||||
|
throw new RuntimeException("Class $class is not a FieldValidator");
|
||||||
|
}
|
||||||
|
$args = [$name, $value];
|
||||||
|
if (!is_null($argCalls)) {
|
||||||
|
if (!is_array($argCalls)) {
|
||||||
|
throw new RuntimeException("argCalls for $class is not an array");
|
||||||
|
}
|
||||||
|
foreach ($argCalls as $i => $argCall) {
|
||||||
|
if (!is_string($argCall) && !is_null($argCall)) {
|
||||||
|
throw new RuntimeException("argCall $i for $class is not a string or null");
|
||||||
|
}
|
||||||
|
if ($argCall) {
|
||||||
|
$args[] = call_user_func([$field, $argCall]);
|
||||||
|
} else {
|
||||||
|
$args[] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$fieldValidators[] = Injector::inst()->createWithArgs($class, $args);
|
||||||
|
}
|
||||||
|
return $fieldValidators;
|
||||||
|
}
|
||||||
|
}
|
33
src/Validation/StringLengthValidator.php
Normal file
33
src/Validation/StringLengthValidator.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Validation;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Validation\ValidationResult;
|
||||||
|
use SilverStripe\Validation\FieldValidator;
|
||||||
|
|
||||||
|
class StringLengthValidator extends FieldValidator
|
||||||
|
{
|
||||||
|
private ?int $minLength;
|
||||||
|
private ?int $maxLength;
|
||||||
|
|
||||||
|
public function __construct(string $name, mixed $value, ?int $minLength = null, ?int $maxLength = null)
|
||||||
|
{
|
||||||
|
parent::__construct($name, $value);
|
||||||
|
$this->minLength = $minLength;
|
||||||
|
$this->maxLength = $maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateValue(ValidationResult $result): ValidationResult
|
||||||
|
{
|
||||||
|
if (!is_null($this->maxLength) && mb_strlen($this->value ?? '') > $this->maxLength) {
|
||||||
|
$message = _t(
|
||||||
|
'SilverStripe\\Forms\\TextField.VALIDATEMAXLENGTH',
|
||||||
|
'The value for {name} must not exceed {maxLength} characters in length',
|
||||||
|
['name' => $this->name, 'maxLength' => $this->maxLength]
|
||||||
|
);
|
||||||
|
$result->addFieldError($this->name, $message);
|
||||||
|
}
|
||||||
|
// TODO: minlength check
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user