This commit is contained in:
Steve Boyd 2024-10-01 05:30:56 +00:00 committed by GitHub
commit e6ecd63069
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 771 additions and 64 deletions

View File

@ -2,6 +2,8 @@
Name: corefieldtypes
---
SilverStripe\Core\Injector\Injector:
NullableVarchar:
class: SilverStripe\ORM\FieldType\DBNullableVarchar
Boolean:
class: SilverStripe\ORM\FieldType\DBBoolean
Currency:
@ -20,6 +22,8 @@ SilverStripe\Core\Injector\Injector:
class: SilverStripe\ORM\FieldType\DBDecimal
Double:
class: SilverStripe\ORM\FieldType\DBDouble
Email:
class: SilverStripe\ORM\FieldType\DBEmail
Enum:
class: SilverStripe\ORM\FieldType\DBEnum
Float:
@ -36,6 +40,8 @@ SilverStripe\Core\Injector\Injector:
class: SilverStripe\ORM\FieldType\DBHTMLVarchar
Int:
class: SilverStripe\ORM\FieldType\DBInt
MediumInt:
class: SilverStripe\ORM\FieldType\DBMediumInt
BigInt:
class: SilverStripe\ORM\FieldType\DBBigInt
Locale:

View File

@ -47,6 +47,7 @@
"symfony/dom-crawler": "^7.0",
"symfony/filesystem": "^7.0",
"symfony/http-foundation": "^7.0",
"symfony/intl": "^7.0",
"symfony/mailer": "^7.0",
"symfony/mime": "^7.0",
"symfony/translation": "^7.0",

View File

@ -2,6 +2,7 @@
namespace SilverStripe\Core\Injector;
use Exception;
use InvalidArgumentException;
/**
@ -23,6 +24,10 @@ class InjectionCreator implements Factory
// Ensure there are no string keys as they cannot be unpacked with the `...` operator
$values = array_values($params);
return new $class(...$values);
try {
return new $class(...$values);
} catch (Exception $e) {
throw $e;
}
}
}

View File

@ -2,11 +2,18 @@
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
{
private static array $field_validators = [
[
'class' => EmailValidator::class,
],
];
protected $inputType = 'email';
/**
@ -17,39 +24,6 @@ class EmailField extends TextField
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()
{
$rules = parent::getSchemaValidation();

View File

@ -15,6 +15,8 @@ use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\View\AttributesHTML;
use SilverStripe\View\SSViewer;
use SilverStripe\Model\ModelData;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Validation\FieldValidator;
/**
* Represents a field in a form.
@ -275,6 +277,8 @@ class FormField extends RequestHandler
'Description' => 'HTMLFragment',
];
private static array $field_validators = [];
/**
* Structured schema state representing the FormField's current data and validation.
* 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
* is valid or not based on the value.
* Subclasses can define an existing FieldValidatorClass to validate the FormField value
* They may also override this method to provide custom validation logic
*
* @param Validator $validator
* @return bool
*/
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);
}
/**

View File

@ -2,6 +2,8 @@
namespace SilverStripe\Forms;
use SilverStripe\Validation\StringValidator;
/**
* Text input field.
*/
@ -14,6 +16,13 @@ class TextField extends FormField implements TippableFieldInterface
protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_TEXT;
private static array $field_validators = [
[
'class' => StringValidator::class,
'argCalls' => [null, 'getMaxLength'],
]
];
/**
* @var Tip|null A tip to render beside the input
*/
@ -117,31 +126,6 @@ class TextField extends FormField implements TippableFieldInterface
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()
{
$rules = parent::getSchemaValidation();

View File

@ -1230,6 +1230,15 @@ class DataObject extends ModelData implements DataObjectInterface, i18nEntityPro
public function validate()
{
$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);
return $result;
}

View File

@ -50,6 +50,16 @@ class DBDecimal extends DBField
return floor($this->value ?? 0.0);
}
public function getWholeSize(): int
{
return $this->wholeSize;
}
public function getDecimalSize(): int
{
return $this->decimalSize;
}
public function requireField(): void
{
$parts = [

View 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;
}
}

View File

@ -10,6 +10,8 @@ use SilverStripe\Forms\TextField;
use SilverStripe\ORM\Filters\SearchFilter;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Model\ModelData;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
/**
* Single field in the database.
@ -43,7 +45,6 @@ use SilverStripe\Model\ModelData;
*/
abstract class DBField extends ModelData implements DBIndexable
{
/**
* Raw value of this field
*/
@ -99,6 +100,8 @@ abstract class DBField extends ModelData implements DBIndexable
'ProcessedRAW' => 'HTMLFragment',
];
private static array $field_validators = [];
/**
* Default value in the database.
* 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
* for form scaffolding.

View File

@ -11,6 +11,8 @@ use SilverStripe\Model\ArrayData;
/**
* Represents a signed 32 bit integer field.
*
* Ints are always signed i.e. they can be negative
*/
class DBInt extends DBField
{

View File

@ -0,0 +1,24 @@
<?php
namespace SilverStripe\ORM\FieldType;
use SilverStripe\ORM\DB;
/**
* Represents a signed 32 bit integer field.
*/
class DBMediumInt extends DBInt
{
public function requireField(): void
{
$parts = [
'datatype' => 'int',
'precision' => 8,
'null' => 'not null',
'default' => $this->defaultVal,
'arrayValue' => $this->arrayValue
];
$values = ['type' => 'int', 'parts' => $parts];
DB::require_field($this->tableName, $this->name, $values);
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace SilverStripe\ORM\FieldType;
class DBNullableVarchar extends DBVarchar
{
public function __construct(?string $name = null, int $size = 255, array $options = [])
{
$options['nullifyEmpty'] = false;
parent::__construct($name, $size, $options);
}
}

View File

@ -8,6 +8,7 @@ use SilverStripe\Forms\NullableField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\Connect\MySQLDatabase;
use SilverStripe\ORM\DB;
use SilverStripe\Validation\StringValidator;
/**
* 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
{
private static array $field_validators = [
[
'class' => StringValidator::class,
'argCalls' => [null, 'getSize'],
]
];
private static array $casting = [
'Initial' => 'Text',
'URL' => 'Text',

View File

@ -0,0 +1,36 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Core\Validation\ConstraintValidator;
use SilverStripe\Validation\StringValidator;
/**
* Abstract class for validators that use Symfony constraints
*/
abstract class AbstractSymfonyValidator extends StringValidator
{
protected function validateValue(ValidationResult $result): ValidationResult
{
$result = parent::validateValue($result);
if (!$result->isValid()) {
return $result;
}
$message = _t(__CLASS__ . '.INVALID', 'Invalid email address');
$constraintClass = $this->getConstraintClass();
$constraint = new $constraintClass(message: $message);
$validationResult = ConstraintValidator::validate($this->value, $constraint, $this->name);
return $result->combineAnd($validationResult);
}
/**
* The symfony constraint class to use
*/
abstract protected function getConstraintClass(): string;
/**
* The message to use when the value is invalid
*/
abstract protected function getMessage(): string;
}

View File

@ -0,0 +1,27 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
class BooleanValidator extends FieldValidator
{
private const VALID_VALUES = [
true,
false,
1,
0,
'1',
'0'
];
protected function validateValue(ValidationResult $result): ValidationResult
{
if (!in_array($this->value, self::VALID_VALUES, true)) {
$message = _t(__CLASS__ . '.INVALID', 'Invalid value');
$result->addFieldError($this->name, $message);
}
return $result;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
class CompositeValidator extends FieldValidator
{
// TODO FieldList / iterable or similar
private mixed $children;
public function __construct(string $name, mixed $value, mixed $children = null)
{
parent::__construct($name, $value);
$this->children = $children;
}
protected function validateValue(ValidationResult $result): ValidationResult
{
foreach ($this->children as $child) {
$result->combineAnd($child->validate());
}
return $result;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
/**
* Validates that a value is a valid date, which means that
* - it follows the PHP date format Y-m-d
* - it follows the ISO format y-MM-dd i.e. DBDate::ISO_DATE
*/
class DateValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
// TODO: does this actually work? - symfony code lacks Y-m-d format
return Constraints\Date::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid date');
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
/**
* Validates that a value is a valid date, which means that
* - it follows the PHP date format Y-m-d H:i:s
* - it follows the ISO format 'y-MM-dd HH:mm:ss' i.e. DBDateTime::ISO_DATETIME
*/
class DatetimeValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
return Constraints\DateTime::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid date/time');
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\NumericValidator;
class DecimalValidator extends NumericValidator
{
/**
* Whole number size e.g. For Decimal(9,2) this would be 9
*/
private int $wholeSize;
/**
* Decimal size e.g. For Decimal(9,2) this would be 2
*/
private int $decimalSize;
public function __construct(string $name, mixed $value, int $wholeSize, int $decimalSize)
{
parent::__construct($name, $value);
$this->wholeSize = $wholeSize;
$this->decimalSize = $decimalSize;
}
protected function validateValue(ValidationResult $result): ValidationResult
{
$result = parent::validateValue($result);
if (!$result->isValid()) {
return $result;
}
// Example of how digits are stored in the database
// Decimal(9,2) is allowed a total of 9 digits, and will always round to 2 decimal places
// This means it has a maximum 7 digits before the decimal point
//
// Valid
// 1234567.99
// 9999999.99
// -9999999.99
// 1234567.999 - will round to 1234568.00
//
// Not valid
// 12345678.9 - 8 digits the before the decimal point
// 1234567.891 - 10 digits total
// 9999999.999 - would be rounted to 10000000.00 which exceeds the 9 digits
// Convert to absolute value - any the minus sign is not counted
$absValue = abs($this->value);
// Round to the decimal size which is what the database will do
$rounded = round($absValue, $this->decimalSize);
// Count this number of digits - note the minus 1 for the decimal point
$digitCount = strlen((string) $rounded) - 1;
if ($digitCount > $this->wholeSize) {
$message = _t(__CLASS__ . '.TOOLARGE', 'Number is too large');
$result->addFieldError($this->name, $message);
}
return $result;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
class EmailValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
return Constraints\Email::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid email address');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
class EnumValidator extends FieldValidator
{
private array $allowedValues;
public function __construct(string $name, mixed $value, array $allowedValues)
{
parent::__construct($name, $value);
$this->allowedValues = $allowedValues;
}
protected function validateValue(ValidationResult $result): ValidationResult
{
if (!in_array($this->value, $this->allowedValues)) {
$message = _t(__CLASS__ . '.NOTALLOWED', 'Not an allowed value');
$result->addFieldError($this->name, $message);
}
return $result;
}
}

View File

@ -0,0 +1,76 @@
<?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;
}
/**
* Validate the value
*/
public function validate(): ValidationResult
{
$result = ValidationResult::create();
$result = $this->validateValue($result);
return $result;
}
/**
* Inner validatation method that that is implemented by subclasses
*/
abstract protected function validateValue(ValidationResult $result): ValidationResult;
/**
* Create FieldValidators for a field based on the field's configuration
*/
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;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\NumericValidator;
class IntValidator extends NumericValidator
{
/**
* Minimum size of the number
*/
private int $minValue;
/**
* Maximum size of the number
*/
private ?int $maxValue;
public function __construct(string $name, mixed $value, int $minValue, int $maxValue)
{
parent::__construct($name, $value);
$this->minValue = $minValue;
$this->maxValue = $maxValue;
}
protected function validateValue(ValidationResult $result): ValidationResult
{
$result = parent::validateValue($result);
if (!$result->isValid()) {
return $result;
}
if ($this->value < $this->minValue) {
$message = _t(__CLASS__ . '.TOOSMALL', 'Value is too small');
$result->addFieldError($this->name, $message);
} elseif ($this->value > $this->maxValue) {
$message = _t(__CLASS__ . '.TOOLARGE', 'Value is too large');
$result->addFieldError($this->name, $message);
}
return $result;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
class IpValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
return Constraints\Ip::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid IP address');
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
/**
* Validates that a value is a valid locale, e.g. de, de_DE)
*/
class LocaleValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
return Constraints\Locale::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid locale');
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
class NumericValidator extends FieldValidator
{
protected function validateValue(ValidationResult $result): ValidationResult
{
if (!is_numeric($this->value)) {
$message = _t(__CLASS__ . '.NOTNUMERIC', 'Must be a number');
$result->addFieldError($this->name, $message);
return $result;
}
return $result;
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
/**
* Validates that a value is a string and optionally checks its multi-byte length.
*/
class StringValidator extends FieldValidator
{
/**
* The minimum length of the string
*/
private ?int $minLength;
/**
* The maximum length of the string
*/
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
{
// Allow blank values
if (!$this->value) {
return $result;
}
if (!is_string($this->value)) {
$message = _t(
__CLASS__ . '.INVALID',
'{name} must be a string',
['name' => $this->name]
);
$result->addFieldError($this->name, $message);
}
$len = mb_strlen($this->value);
if (!is_null($this->minLength) && $len < $this->minLength) {
$message = _t(
__CLASS__ . '.TOOSHORT',
'{name} must have at least {minLength} characters',
['name' => $this->name, 'minLength' => $this->minLength]
);
$result->addFieldError($this->name, $message);
}
if (!is_null($this->maxLength) && $len > $this->maxLength) {
$message = _t(
__CLASS__ . '.TOOLONG',
'{name} cannot have more than {maxLength} characters',
['name' => $this->name, 'maxLength' => $this->maxLength]
);
$result->addFieldError($this->name, $message);
}
return $result;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
/**
* Validates that a value is a valid date, which means that
* - it follows the PHP date format H:i:s
* - it follows the ISO format 'HH:mm:ss' i.e. DBTime::ISO_TIME
*/
class TimeValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
// TODO: does this actually work? - symfony code lacks H:i:s format
return Constraints\Time::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid time');
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace SilverStripe\Validation;
use Symfony\Component\Validator\Constraints;
use SilverStripe\Validation\AbstractSymfonyValidator;
class UrlValidator extends AbstractSymfonyValidator
{
protected function getConstraintClass(): string
{
return Constraints\Url::class;
}
protected function getMessage(): string
{
return _t(__CLASS__ . '.INVALID', 'Invalid URL');
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Validation\legacy_DateValidator;
class legacy_DateTimeValidator extends legacy_DateValidator
{
protected function getFormat()
{
return 'Y-m-d H:i:s';
}
protected function getMessage()
{
return _t(__CLASS__ . '.INVALID', 'Invalid date/time');
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Validation\FieldValidator;
class legacy_DateValidator extends FieldValidator
{
protected function validateValue(ValidationResult $result): ValidationResult
{
// Allow blank values
if (!$this->value) {
return $result;
}
// Attempt to parse the date. If this fails, the date is invalid.
$date = date_create_from_format($this->getFormat(), $this->value);
if ($date === false) {
$result->addFieldError($this->name, $this->getMessage());
}
return $result;
}
/**
* Get the PHP date format to use for date parsing
*/
protected function getFormat()
{
// This validator uses PHP date format codes
// The IntlDateFormatter used by DBDate uses ISO date format codes instead of PHP date format codes
// For ISO format codes see
// http://framework.zend.com/manual/1.12/en/zend.date.constants.html#zend.date.constants.selfdefinedformats
// For PHP format codes see https://www.php.net/manual/en/datetime.format.php
return 'Y-m-d';
}
/**
* Get the error message to use when validation fails
*/
protected function getMessage()
{
return _t(__CLASS__ . '.INVALID', 'Invalid date');
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\Validation;
use SilverStripe\Validation\legacy_DateValidator;
class legacy_TimeValidator extends legacy_DateValidator
{
protected function getFormat()
{
return 'H:i:s';
}
protected function getMessage()
{
return _t(__CLASS__ . '.INVALID', 'Invalid time');
}
}