mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Compare commits
2 Commits
4d5a5a15a2
...
211c9cb246
Author | SHA1 | Date | |
---|---|---|---|
|
211c9cb246 | ||
|
4631aa0067 |
@ -20,6 +20,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 +38,8 @@ SilverStripe\Core\Injector\Injector:
|
||||
class: SilverStripe\ORM\FieldType\DBHTMLVarchar
|
||||
Int:
|
||||
class: SilverStripe\ORM\FieldType\DBInt
|
||||
IP:
|
||||
class: SilverStripe\ORM\FieldType\DBIp
|
||||
BigInt:
|
||||
class: SilverStripe\ORM\FieldType\DBBigInt
|
||||
Locale:
|
||||
@ -58,6 +62,8 @@ SilverStripe\Core\Injector\Injector:
|
||||
class: SilverStripe\ORM\FieldType\DBText
|
||||
Time:
|
||||
class: SilverStripe\ORM\FieldType\DBTime
|
||||
URL:
|
||||
class: SilverStripe\ORM\FieldType\DBUrl
|
||||
Varchar:
|
||||
class: SilverStripe\ORM\FieldType\DBVarchar
|
||||
Year:
|
||||
|
@ -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",
|
||||
|
@ -35,9 +35,9 @@ class ConstraintValidator
|
||||
/** @var ConstraintViolationInterface $violation */
|
||||
foreach ($violations as $violation) {
|
||||
if ($fieldName) {
|
||||
$result->addFieldError($fieldName, $violation->getMessage());
|
||||
$result->addFieldError($fieldName, $violation->getMessage(), value: $value);
|
||||
} else {
|
||||
$result->addError($violation->getMessage());
|
||||
$result->addError($violation->getMessage(), value: $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\ConstraintValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
|
||||
/**
|
||||
* Abstract class for validators that use Symfony constraints
|
||||
*/
|
||||
abstract class AbstractSymfonyFieldValidator extends StringFieldValidator
|
||||
{
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = parent::validateValue();
|
||||
if (!$result->isValid()) {
|
||||
return $result;
|
||||
}
|
||||
$constraintClass = $this->getConstraintClass();
|
||||
$args = [
|
||||
...$this->getContraintNamedArgs(),
|
||||
'message' => $this->getMessage(),
|
||||
];
|
||||
$constraint = new $constraintClass(...$args);
|
||||
$validationResult = ConstraintValidator::validate($this->value, $constraint, $this->name);
|
||||
return $result->combineAnd($validationResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* The symfony constraint class to use
|
||||
*/
|
||||
abstract protected function getConstraintClass(): string;
|
||||
|
||||
/**
|
||||
* The named args to pass to the constraint
|
||||
* Defined named args as assoc array keys
|
||||
*/
|
||||
protected function getContraintNamedArgs(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* The message to use when the value is invalid
|
||||
*/
|
||||
abstract protected function getMessage(): string;
|
||||
}
|
36
src/Core/Validation/FieldValidation/BigIntFieldValidator.php
Normal file
36
src/Core/Validation/FieldValidation/BigIntFieldValidator.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
|
||||
|
||||
class BigIntFieldValidator extends IntFieldValidator
|
||||
{
|
||||
/**
|
||||
* The minimum value for a signed 64-bit integer.
|
||||
* Defined as string instead of int otherwise will end up as a float
|
||||
* on 64-bit systems if defined as an int
|
||||
*/
|
||||
private const MIN_64_BIT_INT = '-9223372036854775808';
|
||||
|
||||
/**
|
||||
* The maximum value for a signed 64-bit integer.
|
||||
*/
|
||||
private const MAX_64_BIT_INT = '9223372036854775807';
|
||||
|
||||
public function __construct(
|
||||
string $name,
|
||||
mixed $value,
|
||||
?int $minValue = null,
|
||||
?int $maxValue = null
|
||||
) {
|
||||
if (is_null($minValue)) {
|
||||
// Casting the string const to an int will properly return an int on 64-bit systems
|
||||
$minValue = (int) BigIntFieldValidator::MIN_64_BIT_INT;
|
||||
}
|
||||
if (is_null($maxValue)) {
|
||||
$maxValue = (int) BigIntFieldValidator::MAX_64_BIT_INT;
|
||||
}
|
||||
parent::__construct($name, $value, $minValue, $maxValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
|
||||
/**
|
||||
* Validates value is boolean stored as an integer i.e. 1 or 0
|
||||
* true and false are not valid values
|
||||
*/
|
||||
class BooleanFieldValidator extends FieldValidator
|
||||
{
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
if ($this->value !== true && $this->value !== false) {
|
||||
$message = _t(__CLASS__ . '.INVALID', 'Invalid value');
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidationInterface;
|
||||
|
||||
class CompositeFieldValidator extends FieldValidator
|
||||
{
|
||||
public function __construct(string $name, mixed $value)
|
||||
{
|
||||
parent::__construct($name, $value);
|
||||
if (!is_iterable($value)) {
|
||||
throw new InvalidArgumentException('Value must be iterable');
|
||||
}
|
||||
foreach ($value as $child) {
|
||||
if (!is_a($child, FieldValidationInterface::class)) {
|
||||
throw new InvalidArgumentException('Child is not a' . FieldValidationInterface::class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
foreach ($this->value as $child) {
|
||||
$result->combineAnd($child->validate());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
40
src/Core/Validation/FieldValidation/DateFieldValidator.php
Normal file
40
src/Core/Validation/FieldValidation/DateFieldValidator.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
|
||||
/**
|
||||
* Validates that a value is a valid date, which means that it follows the equivalent formats:
|
||||
* - PHP date format Y-m-d
|
||||
* - SO format y-MM-dd i.e. DBDate::ISO_DATE
|
||||
* Emtpy values are allowed
|
||||
*/
|
||||
class DateFieldValidator extends FieldValidator
|
||||
{
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
// Allow empty strings
|
||||
if ($this->value === '') {
|
||||
return $result;
|
||||
}
|
||||
// Not using symfony/validator because it was allowing d-m-Y format strings
|
||||
$date = date_parse_from_format($this->getFormat(), $this->value ?? '');
|
||||
if ($date === false || $date['error_count'] > 0 || $date['warning_count'] > 0) {
|
||||
$result->addFieldError($this->name, $this->getMessage(), value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getFormat(): string
|
||||
{
|
||||
return 'Y-m-d';
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid date');
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
|
||||
/**
|
||||
* Validates that a value is a valid date/time, which means that it follows the equivalent formats:
|
||||
* - PHP date format Y-m-d H:i:s
|
||||
* - ISO format 'y-MM-dd HH:mm:ss' i.e. DBDateTime::ISO_DATETIME
|
||||
*/
|
||||
class DatetimeFieldValidator extends DateFieldValidator
|
||||
{
|
||||
protected function getFormat(): string
|
||||
{
|
||||
return 'Y-m-d H:i:s';
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid date/time');
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\NumericFieldValidator;
|
||||
|
||||
class DecimalFieldValidator extends NumericFieldValidator
|
||||
{
|
||||
/**
|
||||
* Whole number size e.g. For Decimal(9,2) this would be 9
|
||||
*/
|
||||
private int $wholeSize;
|
||||
|
||||
/**
|
||||
* Decimal size e.g. For Decimal(5,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 = parent::validateValue();
|
||||
if (!$result->isValid()) {
|
||||
return $result;
|
||||
}
|
||||
// Example of how digits are stored in the database
|
||||
// Decimal(5,2) is allowed a total of 5 digits, and will always round to 2 decimal places
|
||||
// This means it has a maximum 3 digits before the decimal point
|
||||
//
|
||||
// Valid
|
||||
// 123.99
|
||||
// 999.99
|
||||
// -999.99
|
||||
// 123.999 - will round to 124.00
|
||||
//
|
||||
// Not valid
|
||||
// 1234.9 - 4 digits the before the decimal point
|
||||
// 999.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);
|
||||
// Get formatted as a string, which will right pad with zeros to the decimal size
|
||||
$rounded = number_format($rounded, $this->decimalSize, thousands_separator: '');
|
||||
// Count this number of digits - the minus 1 is for the decimal point
|
||||
$digitCount = strlen((string) $rounded) - 1;
|
||||
if ($digitCount > $this->wholeSize) {
|
||||
$message = _t(
|
||||
__CLASS__ . '.TOOLARGE',
|
||||
'Digit count cannot be greater than than {wholeSize}',
|
||||
['wholeSize' => $this->wholeSize]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
19
src/Core/Validation/FieldValidation/EmailFieldValidator.php
Normal file
19
src/Core/Validation/FieldValidation/EmailFieldValidator.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use Symfony\Component\Validator\Constraints;
|
||||
use SilverStripe\Core\Validation\FieldValidation\AbstractSymfonyFieldValidator;
|
||||
|
||||
class EmailFieldValidator extends AbstractSymfonyFieldValidator
|
||||
{
|
||||
protected function getConstraintClass(): string
|
||||
{
|
||||
return Constraints\Email::class;
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid email address');
|
||||
}
|
||||
}
|
31
src/Core/Validation/FieldValidation/EnumFieldValidator.php
Normal file
31
src/Core/Validation/FieldValidation/EnumFieldValidator.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
|
||||
class EnumFieldValidator extends FieldValidator
|
||||
{
|
||||
protected array $allowedValues;
|
||||
|
||||
public function __construct(string $name, mixed $value, array $allowedValues)
|
||||
{
|
||||
parent::__construct($name, $value);
|
||||
$this->allowedValues = $allowedValues;
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
// Allow empty strings
|
||||
if ($this->value === '') {
|
||||
return $result;
|
||||
}
|
||||
if (!in_array($this->value, $this->allowedValues, true)) {
|
||||
$message = _t(__CLASS__ . '.NOTALLOWED', 'Not an allowed value');
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationInterface;
|
||||
|
||||
interface FieldValidationInterface extends ValidationInterface
|
||||
{
|
||||
public function getName(): string;
|
||||
|
||||
public function getValue(): mixed;
|
||||
|
||||
public function getValueForValidation(): mixed;
|
||||
}
|
39
src/Core/Validation/FieldValidation/FieldValidator.php
Normal file
39
src/Core/Validation/FieldValidation/FieldValidator.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\ValidationInterface;
|
||||
|
||||
/**
|
||||
* Abstract class that can be used as a validator for FormFields and DBFields
|
||||
*/
|
||||
abstract class FieldValidator implements ValidationInterface
|
||||
{
|
||||
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();
|
||||
$validationResult = $this->validateValue($result);
|
||||
if (!$validationResult->isValid()) {
|
||||
$result->combineAnd($validationResult);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner validatation method that that is implemented by subclasses
|
||||
*/
|
||||
abstract protected function validateValue(): ValidationResult;
|
||||
}
|
122
src/Core/Validation/FieldValidation/FieldValidatorsTrait.php
Normal file
122
src/Core/Validation/FieldValidation/FieldValidatorsTrait.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use RuntimeException;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidationInterface;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Forms\FormField;
|
||||
|
||||
trait FieldValidatorsTrait
|
||||
{
|
||||
/**
|
||||
* FieldValidators configuration for the field, which is either a FormField or DBField
|
||||
*
|
||||
* Each item in the array can be one of the following
|
||||
* a) MyFieldValidator::class,
|
||||
* b) MyFieldValidator::class => [null, 'getMyArg'],
|
||||
* c) MyFieldValidator::class => null,
|
||||
*
|
||||
* a) Will create a FieldValidator and pass the name and value of the field as args to the constructor
|
||||
* b) Will create a FieldValidator and pass the name, value, make a pass additional args, calling each
|
||||
* non-null value on the field e.g. it will skip the first arg and call $field->getMyArg() for the second arg
|
||||
* c) Will disable a previously set FieldValidator. This is useful to disable a FieldValidator that was set
|
||||
* on a parent class
|
||||
*
|
||||
* You may only have a single instance of a FieldValidator class per field
|
||||
*/
|
||||
private static array $field_validators = [];
|
||||
|
||||
/**
|
||||
* Validate this field
|
||||
*/
|
||||
public function validate(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
// Skip validation if the field value is null
|
||||
if ($this->getValue() === null) {
|
||||
return $result;
|
||||
}
|
||||
$fieldValidators = $this->getFieldValidators();
|
||||
foreach ($fieldValidators as $fieldValidator) {
|
||||
$validationResult = $fieldValidator->validate();
|
||||
if (!$validationResult->isValid()) {
|
||||
$result->combineAnd($validationResult);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get FieldValidators based on `field_validators` configuration
|
||||
*/
|
||||
private function getFieldValidators(): array
|
||||
{
|
||||
$fieldValidators = [];
|
||||
// Used to disable a validator that was previously set with an int index
|
||||
$disabledClasses = [];
|
||||
$interface = FieldValidationInterface::class;
|
||||
// temporary check, will make FormField implement FieldValidationInterface in a future PR
|
||||
$tmp = FormField::class;
|
||||
if (!is_a($this, $interface) && !is_a($this, $tmp)) {
|
||||
$class = get_class($this);
|
||||
throw new RuntimeException("Class $class does not implement interface $interface");
|
||||
}
|
||||
/** @var FieldValidationInterface|Configurable $this */
|
||||
$name = $this->getName();
|
||||
$value = $this->getValueForValidation();
|
||||
// Field name is required for FieldValidators when called ValidationResult::addFieldMessage()
|
||||
if ($name === '') {
|
||||
throw new RuntimeException('Field name is blank');
|
||||
}
|
||||
$classes = [];
|
||||
$config = $this->config()->get('field_validators');
|
||||
foreach ($config as $indexOrClass => $classOrArgCallsOrDisable) {
|
||||
$class = '';
|
||||
$argCalls = [];
|
||||
$disable = false;
|
||||
if (is_int($indexOrClass)) {
|
||||
$class = $classOrArgCallsOrDisable;
|
||||
} else {
|
||||
$class = $indexOrClass;
|
||||
$argCalls = $classOrArgCallsOrDisable;
|
||||
$disable = $classOrArgCallsOrDisable === null;
|
||||
}
|
||||
if ($disable) {
|
||||
$disabledClasses[$class] = true;
|
||||
continue;
|
||||
} else {
|
||||
if (isset($disabledClasses[$class])) {
|
||||
unset($disabledClasses[$class]);
|
||||
}
|
||||
}
|
||||
if (!is_a($class, FieldValidator::class, true)) {
|
||||
throw new RuntimeException("Class $class is not a FieldValidator");
|
||||
}
|
||||
if (!is_array($argCalls)) {
|
||||
throw new RuntimeException("argCalls for FieldValidator $class is not an array");
|
||||
}
|
||||
$classes[$class] = $argCalls;
|
||||
}
|
||||
foreach (array_keys($disabledClasses) as $class) {
|
||||
unset($classes[$class]);
|
||||
}
|
||||
foreach ($classes as $class => $argCalls) {
|
||||
$args = [$name, $value];
|
||||
foreach ($argCalls as $i => $argCall) {
|
||||
if (!is_string($argCall) && !is_null($argCall)) {
|
||||
throw new RuntimeException("argCall $i for FieldValidator $class is not a string or null");
|
||||
}
|
||||
if ($argCall) {
|
||||
$args[] = call_user_func([$this, $argCall]);
|
||||
} else {
|
||||
$args[] = null;
|
||||
}
|
||||
}
|
||||
$fieldValidators[$class] = Injector::inst()->createWithArgs($class, $args);
|
||||
}
|
||||
return array_values($fieldValidators);
|
||||
}
|
||||
}
|
46
src/Core/Validation/FieldValidation/IntFieldValidator.php
Normal file
46
src/Core/Validation/FieldValidation/IntFieldValidator.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\NumericFieldValidator;
|
||||
|
||||
class IntFieldValidator extends NumericFieldValidator
|
||||
{
|
||||
/**
|
||||
* The minimum value for a signed 32-bit integer.
|
||||
* Defined as string instead of int because be cast to a float
|
||||
* on 32-bit systems if defined as an int
|
||||
*/
|
||||
private const MIN_32_BIT_INT = '-2147483648';
|
||||
|
||||
/**
|
||||
* The maximum value for a signed 32-bit integer.
|
||||
*/
|
||||
private const MAX_32_BIT_INT = '2147483647';
|
||||
|
||||
public function __construct(
|
||||
string $name,
|
||||
mixed $value,
|
||||
?int $minValue = null,
|
||||
?int $maxValue = null
|
||||
) {
|
||||
if (is_null($minValue)) {
|
||||
$minValue = (int) IntFieldValidator::MIN_32_BIT_INT;
|
||||
}
|
||||
if (is_null($maxValue)) {
|
||||
$maxValue = (int) IntFieldValidator::MAX_32_BIT_INT;
|
||||
}
|
||||
parent::__construct($name, $value, $minValue, $maxValue);
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = parent::validateValue();
|
||||
if (!is_int($this->value)) {
|
||||
$message = _t(__CLASS__ . '.WRONGTYPE', 'Must be an integer');
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
30
src/Core/Validation/FieldValidation/IpFieldValidator.php
Normal file
30
src/Core/Validation/FieldValidation/IpFieldValidator.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use Symfony\Component\Validator\Constraints;
|
||||
use SilverStripe\Core\Validation\FieldValidation\AbstractSymfonyFieldValidator;
|
||||
|
||||
/**
|
||||
* Validator for IP addresses. Accepts both IPv4 and IPv6.
|
||||
*/
|
||||
class IpFieldValidator extends AbstractSymfonyFieldValidator
|
||||
{
|
||||
protected function getConstraintClass(): string
|
||||
{
|
||||
return Constraints\Ip::class;
|
||||
}
|
||||
|
||||
protected function getContraintNamedArgs(): array
|
||||
{
|
||||
return [
|
||||
// Allow both IPv4 and IPv6
|
||||
'version' => Constraints\Ip::ALL,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid IP address');
|
||||
}
|
||||
}
|
22
src/Core/Validation/FieldValidation/LocaleFieldValidator.php
Normal file
22
src/Core/Validation/FieldValidation/LocaleFieldValidator.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use Symfony\Component\Validator\Constraints;
|
||||
use SilverStripe\Core\Validation\FieldValidation\AbstractSymfonyFieldValidator;
|
||||
|
||||
/**
|
||||
* Validates that a value is a valid locale, e.g. de, de_DE)
|
||||
*/
|
||||
class LocaleFieldValidator extends AbstractSymfonyFieldValidator
|
||||
{
|
||||
protected function getConstraintClass(): string
|
||||
{
|
||||
return Constraints\Locale::class;
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid locale');
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
|
||||
class MultiEnumFieldValidator extends EnumFieldValidator
|
||||
{
|
||||
public function __construct(string $name, mixed $value, array $allowedValues)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
throw new InvalidArgumentException('Value must be an array');
|
||||
}
|
||||
parent::__construct($name, $value, $allowedValues);
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
foreach ($this->value as $value) {
|
||||
if (!in_array($value, $this->allowedValues, true)) {
|
||||
$message = _t(__CLASS__ . '.NOTALLOWED', 'Not an allowed value');
|
||||
$result->addFieldError($this->name, $message, value: $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
|
||||
class NumericFieldValidator extends FieldValidator
|
||||
{
|
||||
/**
|
||||
* 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 = null,
|
||||
?int $maxValue = null
|
||||
) {
|
||||
$this->minValue = $minValue;
|
||||
$this->maxValue = $maxValue;
|
||||
parent::__construct($name, $value);
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
if (!is_numeric($this->value) || is_string($this->value)) {
|
||||
// Must be a numeric value, though not as a numeric string
|
||||
$message = _t(__CLASS__ . '.WRONGTYPE', 'Must be numeric');
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
return $result;
|
||||
} elseif (isset($this->minValue) && $this->value < $this->minValue) {
|
||||
$message = _t(
|
||||
__CLASS__ . '.TOOSMALL',
|
||||
'Value cannot be less than {minValue}',
|
||||
['minValue' => $this->minValue]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
} elseif (isset($this->maxValue) && $this->value > $this->maxValue) {
|
||||
$message = _t(
|
||||
__CLASS__ . '.TOOLARGE',
|
||||
'Value cannot be greater than {maxValue}',
|
||||
['maxValue' => $this->maxValue]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
65
src/Core/Validation/FieldValidation/StringFieldValidator.php
Normal file
65
src/Core/Validation/FieldValidation/StringFieldValidator.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
|
||||
|
||||
/**
|
||||
* Validates that a value is a string and optionally checks its multi-byte length.
|
||||
*/
|
||||
class StringFieldValidator 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);
|
||||
if ($minLength && $minLength < 0) {
|
||||
throw new InvalidArgumentException('minLength must be greater than or equal to 0');
|
||||
}
|
||||
$this->minLength = $minLength;
|
||||
$this->maxLength = $maxLength;
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = ValidationResult::create();
|
||||
if (!is_string($this->value)) {
|
||||
$message = _t(__CLASS__ . '.WRONGTYPE', 'Must be a string');
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
return $result;
|
||||
}
|
||||
$len = mb_strlen($this->value);
|
||||
if (!is_null($this->minLength) && $len < $this->minLength) {
|
||||
$message = _t(
|
||||
__CLASS__ . '.TOOSHORT',
|
||||
'Must have at least {minLength} characters',
|
||||
['minLength' => $this->minLength]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
if (!is_null($this->maxLength) && $len > $this->maxLength) {
|
||||
$message = _t(
|
||||
__CLASS__ . '.TOOLONG',
|
||||
'Can not have more than {maxLength} characters',
|
||||
['maxLength' => $this->maxLength]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
23
src/Core/Validation/FieldValidation/TimeFieldValidator.php
Normal file
23
src/Core/Validation/FieldValidation/TimeFieldValidator.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
|
||||
/**
|
||||
* Validates that a value is a valid time, which means that it follows the equivalent formats:
|
||||
* - PHP date format H:i:s
|
||||
* - ISO format 'HH:mm:ss' i.e. DBTime::ISO_TIME
|
||||
*/
|
||||
class TimeFieldValidator extends DateFieldValidator
|
||||
{
|
||||
protected function getFormat(): string
|
||||
{
|
||||
return 'H:i:s';
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid time');
|
||||
}
|
||||
}
|
19
src/Core/Validation/FieldValidation/UrlFieldValidator.php
Normal file
19
src/Core/Validation/FieldValidation/UrlFieldValidator.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use Symfony\Component\Validator\Constraints;
|
||||
use SilverStripe\Core\Validation\FieldValidation\AbstractSymfonyFieldValidator;
|
||||
|
||||
class UrlFieldValidator extends AbstractSymfonyFieldValidator
|
||||
{
|
||||
protected function getConstraintClass(): string
|
||||
{
|
||||
return Constraints\Url::class;
|
||||
}
|
||||
|
||||
protected function getMessage(): string
|
||||
{
|
||||
return _t(__CLASS__ . '.INVALID', 'Invalid URL');
|
||||
}
|
||||
}
|
42
src/Core/Validation/FieldValidation/YearFieldValidator.php
Normal file
42
src/Core/Validation/FieldValidation/YearFieldValidator.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Core\Validation\FieldValidation\NumericFieldValidator;
|
||||
|
||||
/**
|
||||
* Validates that a field is an integer year between two dates, or 0 for a null value.
|
||||
*/
|
||||
class YearFieldValidator extends IntFieldValidator
|
||||
{
|
||||
private ?int $minValue;
|
||||
|
||||
public function __construct(
|
||||
string $name,
|
||||
mixed $value,
|
||||
?int $minValue = null,
|
||||
?int $maxValue = null
|
||||
) {
|
||||
$this->minValue = $minValue;
|
||||
parent::__construct($name, $value, 0, $maxValue);
|
||||
}
|
||||
|
||||
protected function validateValue(): ValidationResult
|
||||
{
|
||||
$result = parent::validateValue();
|
||||
if ($this->value === 0) {
|
||||
return $result;
|
||||
}
|
||||
if ($this->minValue && $this->value < $this->minValue) {
|
||||
// Uses the same translation key as NumericFieldValidator
|
||||
$message = _t(
|
||||
NumericFieldValidator::class . '.TOOSMALL',
|
||||
'Value cannot be less than {minValue}',
|
||||
['minValue' => $this->minValue]
|
||||
);
|
||||
$result->addFieldError($this->name, $message, value: $this->value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
10
src/Core/Validation/ValidationInterface.php
Normal file
10
src/Core/Validation/ValidationInterface.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Validation;
|
||||
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
|
||||
interface ValidationInterface
|
||||
{
|
||||
public function validate(): ValidationResult;
|
||||
}
|
@ -46,6 +46,11 @@ class ValidationResult
|
||||
*/
|
||||
const CAST_TEXT = 'text';
|
||||
|
||||
/**
|
||||
* Default value of $value parameter
|
||||
*/
|
||||
private const VALUE_UNSET = '_VALUE_UNSET_';
|
||||
|
||||
/**
|
||||
* Is the result valid or not.
|
||||
* Note that there can be non-error messages in the list.
|
||||
@ -71,11 +76,17 @@ class ValidationResult
|
||||
* This can be usedful for ensuring no duplicate messages
|
||||
* @param string|bool $cast Cast type; One of the CAST_ constant definitions.
|
||||
* Bool values will be treated as plain text flag.
|
||||
* @param mixed $value The value that failed validation
|
||||
* @return $this
|
||||
*/
|
||||
public function addError($message, $messageType = ValidationResult::TYPE_ERROR, $code = null, $cast = ValidationResult::CAST_TEXT)
|
||||
{
|
||||
return $this->addFieldError(null, $message, $messageType, $code, $cast);
|
||||
public function addError(
|
||||
$message,
|
||||
$messageType = ValidationResult::TYPE_ERROR,
|
||||
$code = null,
|
||||
$cast = ValidationResult::CAST_TEXT,
|
||||
$value = ValidationResult::VALUE_UNSET,
|
||||
) {
|
||||
return $this->addFieldError(null, $message, $messageType, $code, $cast, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,6 +100,7 @@ class ValidationResult
|
||||
* This can be usedful for ensuring no duplicate messages
|
||||
* @param string|bool $cast Cast type; One of the CAST_ constant definitions.
|
||||
* Bool values will be treated as plain text flag.
|
||||
* @param mixed $value The value that failed validation
|
||||
* @return $this
|
||||
*/
|
||||
public function addFieldError(
|
||||
@ -96,10 +108,11 @@ class ValidationResult
|
||||
$message,
|
||||
$messageType = ValidationResult::TYPE_ERROR,
|
||||
$code = null,
|
||||
$cast = ValidationResult::CAST_TEXT
|
||||
$cast = ValidationResult::CAST_TEXT,
|
||||
$value = ValidationResult::VALUE_UNSET,
|
||||
) {
|
||||
$this->isValid = false;
|
||||
return $this->addFieldMessage($fieldName, $message, $messageType, $code, $cast);
|
||||
return $this->addFieldMessage($fieldName, $message, $messageType, $code, $cast, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,11 +125,17 @@ class ValidationResult
|
||||
* This can be usedful for ensuring no duplicate messages
|
||||
* @param string|bool $cast Cast type; One of the CAST_ constant definitions.
|
||||
* Bool values will be treated as plain text flag.
|
||||
* @param mixed $value The value that failed validation
|
||||
* @return $this
|
||||
*/
|
||||
public function addMessage($message, $messageType = ValidationResult::TYPE_ERROR, $code = null, $cast = ValidationResult::CAST_TEXT)
|
||||
{
|
||||
return $this->addFieldMessage(null, $message, $messageType, $code, $cast);
|
||||
public function addMessage(
|
||||
$message,
|
||||
$messageType = ValidationResult::TYPE_ERROR,
|
||||
$code = null,
|
||||
$cast = ValidationResult::CAST_TEXT,
|
||||
$value = ValidationResult::VALUE_UNSET,
|
||||
) {
|
||||
return $this->addFieldMessage(null, $message, $messageType, $code, $cast, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,6 +149,7 @@ class ValidationResult
|
||||
* This can be usedful for ensuring no duplicate messages
|
||||
* @param string|bool $cast Cast type; One of the CAST_ constant definitions.
|
||||
* Bool values will be treated as plain text flag.
|
||||
* @param mixed $value The value that failed validation
|
||||
* @return $this
|
||||
*/
|
||||
public function addFieldMessage(
|
||||
@ -137,7 +157,8 @@ class ValidationResult
|
||||
$message,
|
||||
$messageType = ValidationResult::TYPE_ERROR,
|
||||
$code = null,
|
||||
$cast = ValidationResult::CAST_TEXT
|
||||
$cast = ValidationResult::CAST_TEXT,
|
||||
$value = ValidationResult::VALUE_UNSET,
|
||||
) {
|
||||
if ($code && is_numeric($code)) {
|
||||
throw new InvalidArgumentException("Don't use a numeric code '$code'. Use a string.");
|
||||
@ -151,7 +172,9 @@ class ValidationResult
|
||||
'messageType' => $messageType,
|
||||
'messageCast' => $cast,
|
||||
];
|
||||
|
||||
if ($value !== ValidationResult::VALUE_UNSET) {
|
||||
$metadata['value'] = $value;
|
||||
}
|
||||
if ($code) {
|
||||
$this->messages[$code] = $metadata;
|
||||
} else {
|
||||
|
@ -119,10 +119,8 @@ class CompositeField extends FormField
|
||||
* Returns the name (ID) for the element.
|
||||
* If the CompositeField doesn't have a name, but we still want the ID/name to be set.
|
||||
* This code generates the ID from the nested children.
|
||||
*
|
||||
* @return String $name
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
if ($this->name) {
|
||||
return $this->name;
|
||||
|
@ -106,7 +106,7 @@ class FieldGroup extends CompositeField
|
||||
* In some cases the FieldGroup doesn't have a title, but we still want
|
||||
* the ID / name to be set. This code, generates the ID from the nested children
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
if ($this->name) {
|
||||
return $this->name;
|
||||
|
@ -15,6 +15,7 @@ use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\View\AttributesHTML;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidatorsTrait;
|
||||
|
||||
/**
|
||||
* Represents a field in a form.
|
||||
@ -44,6 +45,7 @@ class FormField extends RequestHandler
|
||||
{
|
||||
use AttributesHTML;
|
||||
use FormMessage;
|
||||
use FieldValidatorsTrait;
|
||||
|
||||
/** @see $schemaDataType */
|
||||
const SCHEMA_DATA_TYPE_STRING = 'String';
|
||||
@ -424,12 +426,10 @@ class FormField extends RequestHandler
|
||||
|
||||
/**
|
||||
* Returns the field name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
return $this->name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -443,16 +443,32 @@ class FormField extends RequestHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field value.
|
||||
* Alias of getValue()
|
||||
*
|
||||
* @see FormField::setSubmittedValue()
|
||||
* @return mixed
|
||||
*/
|
||||
public function Value()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field value.
|
||||
*/
|
||||
public function getValue(): mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of this field for field validation
|
||||
*/
|
||||
public function getValueForValidation(): mixed
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to save this form field into the given record.
|
||||
*
|
||||
@ -1231,15 +1247,32 @@ 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;
|
||||
$result = ValidationResult::create();
|
||||
if ($this->getValue() === null) {
|
||||
// Skip field validation if the value is null
|
||||
return $this->extendValidationResult($isValid, $validator);
|
||||
}
|
||||
$fieldValidators = $this->getFieldValidators();
|
||||
foreach ($fieldValidators as $fieldValidator) {
|
||||
$validationResult = $fieldValidator->validate();
|
||||
if (!$validationResult->isValid()) {
|
||||
$result->combineAnd($validationResult);
|
||||
}
|
||||
}
|
||||
if (!$result->isValid()) {
|
||||
$isValid = false;
|
||||
$validator->getResult()->combineAnd($result);
|
||||
}
|
||||
return $this->extendValidationResult($isValid, $validator);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ class SelectionGroup_Item extends CompositeField
|
||||
return $this;
|
||||
}
|
||||
|
||||
function getValue()
|
||||
function getValue(): mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
@ -1238,6 +1238,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;
|
||||
}
|
||||
|
@ -2,18 +2,24 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\BigIntFieldValidator;
|
||||
use SilverStripe\ORM\DB;
|
||||
|
||||
/**
|
||||
* Represents a signed 8 byte integer field. Do note PHP running as 32-bit might not work with Bigint properly, as it
|
||||
* would convert the value to a float when queried from the database since the value is a 64-bit one.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
* @see Int
|
||||
* BigInt is always signed i.e. can be negative
|
||||
* Their range is -9223372036854775808 to 9223372036854775807
|
||||
*/
|
||||
class DBBigInt extends DBInt
|
||||
{
|
||||
private static array $field_validators = [
|
||||
// Remove parent validator and add BigIntValidator instead
|
||||
IntFieldValidator::class => null,
|
||||
BigIntFieldValidator::class,
|
||||
];
|
||||
|
||||
public function requireField(): void
|
||||
{
|
||||
@ -24,7 +30,6 @@ class DBBigInt extends DBInt
|
||||
'default' => $this->defaultVal,
|
||||
'arrayValue' => $this->arrayValue
|
||||
];
|
||||
|
||||
$values = ['type' => 'bigint', 'parts' => $parts];
|
||||
DB::require_field($this->tableName, $this->name, $values);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\BooleanFieldValidator;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FormField;
|
||||
@ -9,13 +10,18 @@ use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Model\ModelData;
|
||||
|
||||
/**
|
||||
* Represents a boolean field.
|
||||
* Represents a boolean field
|
||||
* Values are stored in the database as tinyint i.e. 1 or 0
|
||||
*/
|
||||
class DBBoolean extends DBField
|
||||
{
|
||||
public function __construct(?string $name = null, bool|int $defaultVal = 0)
|
||||
private static array $field_validators = [
|
||||
BooleanFieldValidator::class,
|
||||
];
|
||||
|
||||
public function __construct(?string $name = null, bool $defaultVal = false)
|
||||
{
|
||||
$this->defaultVal = ($defaultVal) ? 1 : 0;
|
||||
$this->setDefaultValue($defaultVal);
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
@ -34,6 +40,13 @@ class DBBoolean extends DBField
|
||||
DB::require_field($this->tableName, $this->name, $values);
|
||||
}
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
parent::setValue($value);
|
||||
$this->value = $this->convertBooleanLikeValue($value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function Nice(): string
|
||||
{
|
||||
return ($this->value) ? _t(__CLASS__ . '.YESANSWER', 'Yes') : _t(__CLASS__ . '.NOANSWER', 'No');
|
||||
@ -51,7 +64,7 @@ class DBBoolean extends DBField
|
||||
if ($this->value instanceof DBField) {
|
||||
$this->value->saveInto($dataObject);
|
||||
} else {
|
||||
$dataObject->__set($fieldName, $this->value ? 1 : 0);
|
||||
$dataObject->__set($fieldName, $this->value ? true : false);
|
||||
}
|
||||
} else {
|
||||
$class = static::class;
|
||||
@ -69,37 +82,50 @@ class DBBoolean extends DBField
|
||||
$anyText = _t(__CLASS__ . '.ANY', 'Any');
|
||||
$source = [
|
||||
'' => $anyText,
|
||||
1 => _t(__CLASS__ . '.YESANSWER', 'Yes'),
|
||||
0 => _t(__CLASS__ . '.NOANSWER', 'No')
|
||||
'1' => _t(__CLASS__ . '.YESANSWER', 'Yes'),
|
||||
'0' => _t(__CLASS__ . '.NOANSWER', 'No')
|
||||
];
|
||||
|
||||
return DropdownField::create($this->name, $title, $source)
|
||||
->setEmptyString($anyText);
|
||||
}
|
||||
|
||||
public function nullValue(): ?int
|
||||
public function nullValue(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function prepValueForDB(mixed $value): array|int|null
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
if (empty($value)) {
|
||||
return 0;
|
||||
}
|
||||
$bool = $this->convertBooleanLikeValue($value);
|
||||
// Ensure a tiny int is returned no matter what e.g. value is an
|
||||
return $bool ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert boolean-like values to boolean
|
||||
* Does not convert non-boolean-like values e.g. array - will be handled by the FieldValidator
|
||||
*/
|
||||
private function convertBooleanLikeValue(mixed $value): mixed
|
||||
{
|
||||
if (is_string($value)) {
|
||||
switch (strtolower($value ?? '')) {
|
||||
switch (strtolower($value)) {
|
||||
case 'false':
|
||||
case 'f':
|
||||
return 0;
|
||||
case '0':
|
||||
return false;
|
||||
case 'true':
|
||||
case 't':
|
||||
return 1;
|
||||
case '1':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return $value ? 1 : 0;
|
||||
if ($value === 0) {
|
||||
return false;
|
||||
}
|
||||
if ($value === 1) {
|
||||
return true;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
|
||||
/**
|
||||
* An alternative to DBClassName that stores the class name as a varchar instead of an enum
|
||||
@ -24,4 +25,8 @@ use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
class DBClassNameVarchar extends DBVarchar
|
||||
{
|
||||
use DBClassNameTrait;
|
||||
|
||||
private static array $field_validators = [
|
||||
EnumFieldValidator::class => ['getEnum'],
|
||||
];
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\CompositeFieldValidator;
|
||||
|
||||
/**
|
||||
* Extend this class when designing a {@link DBField} that doesn't have a 1-1 mapping with a database field.
|
||||
@ -25,6 +26,10 @@ use SilverStripe\Model\ModelData;
|
||||
*/
|
||||
abstract class DBComposite extends DBField
|
||||
{
|
||||
private static array $field_validators = [
|
||||
CompositeFieldValidator::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Similar to {@link DataObject::$db},
|
||||
* holds an array of composite field names.
|
||||
@ -190,6 +195,15 @@ abstract class DBComposite extends DBField
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValueForValidation(): mixed
|
||||
{
|
||||
$fields = [];
|
||||
foreach (array_keys($this->compositeDatabaseFields()) as $fieldName) {
|
||||
$fields[] = $this->dbObject($fieldName);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind this field to the model, and set the underlying table to that of the owner
|
||||
*/
|
||||
|
@ -31,6 +31,9 @@ class DBCurrency extends DBDecimal
|
||||
*/
|
||||
public function Nice(): string
|
||||
{
|
||||
if (!is_float($this->value)) {
|
||||
return '';
|
||||
}
|
||||
$val = static::config()->get('currency_symbol') . number_format(abs($this->value ?? 0.0) ?? 0.0, 2);
|
||||
if ($this->value < 0) {
|
||||
return "($val)";
|
||||
@ -44,6 +47,9 @@ class DBCurrency extends DBDecimal
|
||||
*/
|
||||
public function Whole(): string
|
||||
{
|
||||
if (!is_float($this->value)) {
|
||||
return '';
|
||||
}
|
||||
$val = static::config()->get('currency_symbol') . number_format(abs($this->value ?? 0.0) ?? 0.0, 0);
|
||||
if ($this->value < 0) {
|
||||
return "($val)";
|
||||
@ -53,15 +59,14 @@ class DBCurrency extends DBDecimal
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
$matches = null;
|
||||
if (is_numeric($value)) {
|
||||
$this->value = $value;
|
||||
} elseif (preg_match('/-?\$?[0-9,]+(.[0-9]+)?([Ee][0-9]+)?/', $value ?? '', $matches)) {
|
||||
$this->value = str_replace(['$', ',', static::config()->get('currency_symbol')], '', $matches[0] ?? '');
|
||||
} else {
|
||||
$this->value = 0;
|
||||
parent::setValue($value, $record, $markChanged);
|
||||
if (is_string($this->value)) {
|
||||
$symbol = static::config()->get('currency_symbol');
|
||||
$val = str_replace(['$', ',', $symbol], '', $this->value);
|
||||
if (is_numeric($val)) {
|
||||
$this->value = (float) $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
|
||||
/**
|
||||
* Represents a date field.
|
||||
@ -33,6 +34,7 @@ class DBDate extends DBField
|
||||
{
|
||||
/**
|
||||
* Standard ISO format string for date in CLDR standard format
|
||||
* This is equivalent to php date format "Y-m-d" e.g. 2024-08-31
|
||||
*/
|
||||
public const ISO_DATE = 'y-MM-dd';
|
||||
|
||||
@ -42,13 +44,14 @@ class DBDate extends DBField
|
||||
*/
|
||||
public const ISO_LOCALE = 'en_US';
|
||||
|
||||
private static array $field_validators = [
|
||||
DateFieldValidator::class,
|
||||
];
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
$value = $this->parseDate($value);
|
||||
if ($value === false) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid date: '$value'. Use " . DBDate::ISO_DATE . " to prevent this error."
|
||||
);
|
||||
if ($value !== null) {
|
||||
$value = $this->parseDate($value);
|
||||
}
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
@ -58,15 +61,10 @@ class DBDate extends DBField
|
||||
* Parse timestamp or iso8601-ish date into standard iso8601 format
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string|null|false Formatted date, null if empty but valid, or false if invalid
|
||||
* @return mixed Formatted date, or the original value if it couldn't be parsed
|
||||
*/
|
||||
protected function parseDate(mixed $value): string|null|false
|
||||
protected function parseDate(mixed $value): mixed
|
||||
{
|
||||
// Skip empty values
|
||||
if (empty($value) && !is_numeric($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine value to parse
|
||||
if (is_array($value)) {
|
||||
$source = $value; // parse array
|
||||
@ -74,19 +72,23 @@ class DBDate extends DBField
|
||||
$source = $value; // parse timestamp
|
||||
} else {
|
||||
// Convert US date -> iso, fix y2k, etc
|
||||
$value = $this->fixInputDate($value);
|
||||
if (is_null($value)) {
|
||||
return null;
|
||||
$fixedValue = $this->fixInputDate($value);
|
||||
if ($fixedValue === '') {
|
||||
// Dates with an invalid format will be caught by validator later
|
||||
return $value;
|
||||
}
|
||||
$source = strtotime($value ?? ''); // convert string to timestamp
|
||||
// convert string to timestamp
|
||||
$source = strtotime($fixedValue ?? '');
|
||||
}
|
||||
if ($value === false) {
|
||||
return false;
|
||||
if (!$source && $source !== 0 and $source !== '0') {
|
||||
// Unable to parse date, keep as is so that the validator can catch it later
|
||||
// Note that 0 and '0' are valid dates for Jan 1 1970
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Format as iso8601
|
||||
$formatter = $this->getInternalFormatter();
|
||||
return $formatter->format($source);
|
||||
$ret = $formatter->format($source);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -554,26 +556,14 @@ class DBDate extends DBField
|
||||
|
||||
/**
|
||||
* Fix non-iso dates
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function fixInputDate($value)
|
||||
protected function fixInputDate(string $value): string
|
||||
{
|
||||
// split
|
||||
[$year, $month, $day, $time] = $this->explodeDateString($value);
|
||||
|
||||
if ((int)$year === 0 && (int)$month === 0 && (int)$day === 0) {
|
||||
return null;
|
||||
if (!checkdate((int) $month, (int) $day, (int) $year)) {
|
||||
return '';
|
||||
}
|
||||
// Validate date
|
||||
if (!checkdate($month ?? 0, $day ?? 0, $year ?? 0)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid date: '$value'. Use " . DBDate::ISO_DATE . " to prevent this error."
|
||||
);
|
||||
}
|
||||
|
||||
// Convert to y-m-d
|
||||
// Convert to Y-m-d
|
||||
return sprintf('%d-%02d-%02d%s', $year, $month, $day, $time);
|
||||
}
|
||||
|
||||
@ -591,11 +581,8 @@ class DBDate extends DBField
|
||||
$value ?? '',
|
||||
$matches
|
||||
)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid date: '$value'. Use " . DBDate::ISO_DATE . " to prevent this error."
|
||||
);
|
||||
return [0, 0, 0, ''];
|
||||
}
|
||||
|
||||
$parts = [
|
||||
$matches['first'],
|
||||
$matches['second'],
|
||||
@ -605,11 +592,6 @@ class DBDate extends DBField
|
||||
if ($parts[0] < 1000 && $parts[2] > 1000) {
|
||||
$parts = array_reverse($parts ?? []);
|
||||
}
|
||||
if ($parts[0] < 1000 && (int)$parts[0] !== 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid date: '$value'. Use " . DBDate::ISO_DATE . " to prevent this error."
|
||||
);
|
||||
}
|
||||
$parts[] = $matches['time'];
|
||||
return $parts;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\View\TemplateGlobalProvider;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DatetimeFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
|
||||
/**
|
||||
* Represents a date-time field.
|
||||
@ -39,6 +41,7 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider
|
||||
/**
|
||||
* Standard ISO format string for date and time in CLDR standard format,
|
||||
* with a whitespace separating date and time (common database representation, e.g. in MySQL).
|
||||
* This is equivalent to php date format "Y-m-d H:i:s" e.g. 2024-08-31 09:30:00
|
||||
*/
|
||||
public const ISO_DATETIME = 'y-MM-dd HH:mm:ss';
|
||||
|
||||
@ -48,6 +51,13 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider
|
||||
*/
|
||||
public const ISO_DATETIME_NORMALISED = 'y-MM-dd\'T\'HH:mm:ss';
|
||||
|
||||
private static array $field_validators = [
|
||||
// Remove parent validator
|
||||
DateFieldValidator::class => null,
|
||||
// Add datetime validator
|
||||
DatetimeFieldValidator::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Flag idicating if this field is considered immutable
|
||||
* when this is enabled setting the value of this field will return a new field instance
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\DecimalFieldValidator;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\ORM\DB;
|
||||
@ -12,6 +13,10 @@ use SilverStripe\Model\ModelData;
|
||||
*/
|
||||
class DBDecimal extends DBField
|
||||
{
|
||||
private static array $field_validators = [
|
||||
DecimalFieldValidator::class => ['getWholeSize', 'getDecimalSize'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Whole number size
|
||||
*/
|
||||
@ -22,20 +27,15 @@ class DBDecimal extends DBField
|
||||
*/
|
||||
protected int $decimalSize = 2;
|
||||
|
||||
/**
|
||||
* Default value
|
||||
*/
|
||||
protected float|int|string $defaultValue = 0;
|
||||
|
||||
/**
|
||||
* Create a new Decimal field.
|
||||
*/
|
||||
public function __construct(?string $name = null, ?int $wholeSize = 9, ?int $decimalSize = 2, float|int $defaultValue = 0)
|
||||
public function __construct(?string $name = null, ?int $wholeSize = 9, ?int $decimalSize = 2, float|int $defaultValue = 0.0)
|
||||
{
|
||||
$this->wholeSize = is_int($wholeSize) ? $wholeSize : 9;
|
||||
$this->decimalSize = is_int($decimalSize) ? $decimalSize : 2;
|
||||
|
||||
$this->defaultValue = number_format((float) $defaultValue, $this->decimalSize);
|
||||
$this->setDefaultValue(round($defaultValue, $this->decimalSize));
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
@ -50,12 +50,22 @@ 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 = [
|
||||
'datatype' => 'decimal',
|
||||
'precision' => "$this->wholeSize,$this->decimalSize",
|
||||
'default' => $this->defaultValue,
|
||||
'default' => $this->getDefaultValue(),
|
||||
'arrayValue' => $this->arrayValue
|
||||
];
|
||||
|
||||
@ -67,6 +77,16 @@ class DBDecimal extends DBField
|
||||
DB::require_field($this->tableName, $this->name, $values);
|
||||
}
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
parent::setValue($value, $record, $markChanged);
|
||||
// Cast ints and numeric strings to floats
|
||||
if (is_int($this->value) || (is_string($this->value) && is_numeric($this->value))) {
|
||||
$this->value = (float) $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function saveInto(ModelData $model): void
|
||||
{
|
||||
$fieldName = $this->name;
|
||||
@ -91,25 +111,25 @@ class DBDecimal extends DBField
|
||||
->setScale($this->decimalSize);
|
||||
}
|
||||
|
||||
public function nullValue(): ?int
|
||||
public function nullValue(): float
|
||||
{
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public function prepValueForDB(mixed $value): array|float|int|null
|
||||
{
|
||||
if ($value === true) {
|
||||
return 1;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (empty($value) || !is_numeric($value)) {
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (abs((float) $value - (int) $value) < PHP_FLOAT_EPSILON) {
|
||||
return (int)$value;
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return (float)$value;
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
|
22
src/ORM/FieldType/DBEmail.php
Normal file
22
src/ORM/FieldType/DBEmail.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Forms\EmailField;
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EmailFieldValidator;
|
||||
use SilverStripe\Forms\FormField;
|
||||
|
||||
class DBEmail extends DBVarchar
|
||||
{
|
||||
private static array $field_validators = [
|
||||
EmailFieldValidator::class,
|
||||
];
|
||||
|
||||
public function scaffoldFormField(?string $title = null, array $params = []): ?FormField
|
||||
{
|
||||
$field = EmailField::create($this->name, $title);
|
||||
$field->setMaxLength($this->getSize());
|
||||
return $field;
|
||||
}
|
||||
}
|
@ -3,12 +3,14 @@
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\Forms\SelectField;
|
||||
use SilverStripe\Core\ArrayLib;
|
||||
use SilverStripe\ORM\Connect\MySQLDatabase;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Model\ModelData;
|
||||
|
||||
/**
|
||||
* Class Enum represents an enumeration of a set of strings.
|
||||
@ -17,6 +19,10 @@ use SilverStripe\ORM\DB;
|
||||
*/
|
||||
class DBEnum extends DBString
|
||||
{
|
||||
private static array $field_validators = [
|
||||
EnumFieldValidator::class => ['getEnum'],
|
||||
];
|
||||
|
||||
/**
|
||||
* List of enum values
|
||||
*/
|
||||
@ -73,14 +79,14 @@ class DBEnum extends DBString
|
||||
|
||||
// If there's a default, then use this
|
||||
if ($default && !is_int($default)) {
|
||||
if (in_array($default, $enum ?? [])) {
|
||||
if (in_array($default, $enum)) {
|
||||
$this->setDefault($default);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
"Enum::__construct() The default value '$default' does not match any item in the enumeration"
|
||||
);
|
||||
}
|
||||
} elseif (is_int($default) && $default < count($enum ?? [])) {
|
||||
} elseif (is_int($default) && $default < count($enum)) {
|
||||
// Set to specified index if given
|
||||
$this->setDefault($enum[$default]);
|
||||
} else {
|
||||
|
@ -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\FieldValidation\FieldValidatorsTrait;
|
||||
use SilverStripe\Core\Validation\FieldValidation\FieldValidationInterface;
|
||||
|
||||
/**
|
||||
* Single field in the database.
|
||||
@ -41,8 +43,9 @@ use SilverStripe\Model\ModelData;
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
abstract class DBField extends ModelData implements DBIndexable
|
||||
abstract class DBField extends ModelData implements DBIndexable, FieldValidationInterface
|
||||
{
|
||||
use FieldValidatorsTrait;
|
||||
|
||||
/**
|
||||
* Raw value of this field
|
||||
@ -99,12 +102,14 @@ 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
|
||||
* already existing records after a db-build.
|
||||
*/
|
||||
protected mixed $defaultVal = null;
|
||||
private mixed $defaultValue = null;
|
||||
|
||||
/**
|
||||
* Provide the DBField name and an array of options, e.g. ['index' => true], or ['nullifyEmpty' => false]
|
||||
@ -121,6 +126,8 @@ abstract class DBField extends ModelData implements DBIndexable
|
||||
}
|
||||
$this->setOptions($options);
|
||||
}
|
||||
// Setting value needs to happen below the call to setOptions() in case the default value is set there
|
||||
$this->value = $this->getDefaultValue();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
@ -161,7 +168,7 @@ abstract class DBField extends ModelData implements DBIndexable
|
||||
*
|
||||
* If you try an alter the name a warning will be thrown.
|
||||
*/
|
||||
public function setName(?string $name): static
|
||||
public function setName(string $name): static
|
||||
{
|
||||
if ($this->name && $this->name !== $name) {
|
||||
user_error("DBField::setName() shouldn't be called once a DBField already has a name."
|
||||
@ -189,6 +196,18 @@ abstract class DBField extends ModelData implements DBIndexable
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of this field for field validation
|
||||
*/
|
||||
public function getValueForValidation(): mixed
|
||||
{
|
||||
$value = $this->getValue();
|
||||
if (is_null($value)) {
|
||||
return $this->nullValue();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of this field in various formats.
|
||||
* Used by {@link DataObject->getField()}, {@link DataObject->setCastedField()}
|
||||
@ -214,7 +233,7 @@ abstract class DBField extends ModelData implements DBIndexable
|
||||
*/
|
||||
public function getDefaultValue(): mixed
|
||||
{
|
||||
return $this->defaultVal;
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,7 +241,7 @@ abstract class DBField extends ModelData implements DBIndexable
|
||||
*/
|
||||
public function setDefaultValue(mixed $defaultValue): static
|
||||
{
|
||||
$this->defaultVal = $defaultValue;
|
||||
$this->defaultValue = $defaultValue;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ class DBFloat extends DBField
|
||||
{
|
||||
public function __construct(?string $name = null, float|int $defaultVal = 0)
|
||||
{
|
||||
$this->defaultVal = is_float($defaultVal) ? $defaultVal : (float) 0;
|
||||
$this->setDefaultValue(is_float($defaultVal) ? $defaultVal : (float) 0);
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
@ -57,7 +57,7 @@ class DBFloat extends DBField
|
||||
return $field;
|
||||
}
|
||||
|
||||
public function nullValue(): ?int
|
||||
public function nullValue(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -63,6 +63,11 @@ class DBForeignKey extends DBInt
|
||||
if ($record instanceof DataObject) {
|
||||
$this->object = $record;
|
||||
}
|
||||
// Convert blank string to 0, this is sometimes required when calling DataObject::setCastedValue()
|
||||
// after a form submission where the value is a blank string when no value is selected
|
||||
if ($value === '') {
|
||||
$value = 0;
|
||||
}
|
||||
return parent::setValue($value, $record, $markChanged);
|
||||
}
|
||||
}
|
||||
|
@ -2,32 +2,46 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Model\List\ArrayList;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Model\List\SS_List;
|
||||
use SilverStripe\Model\ArrayData;
|
||||
use SilverStripe\Model\ModelData;
|
||||
|
||||
/**
|
||||
* Represents a signed 32 bit integer field.
|
||||
* Represents a signed 32 bit integer field
|
||||
*
|
||||
* Ints are always signed i.e. they can be negative
|
||||
* Their range is -2147483648 to 2147483647
|
||||
*/
|
||||
class DBInt extends DBField
|
||||
{
|
||||
private static array $field_validators = [
|
||||
IntFieldValidator::class
|
||||
];
|
||||
|
||||
public function __construct(?string $name = null, int $defaultVal = 0)
|
||||
{
|
||||
$this->defaultVal = is_int($defaultVal) ? $defaultVal : 0;
|
||||
|
||||
$this->setDefaultValue($defaultVal);
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure int values are always returned.
|
||||
* This is for mis-configured databases that return strings.
|
||||
*/
|
||||
public function getValue(): ?int
|
||||
public function getField($fieldName): mixed
|
||||
{
|
||||
return (int) $this->value;
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
parent::setValue($value, $record, $markChanged);
|
||||
// Cast int like strings as ints
|
||||
if (is_string($this->value) && preg_match('/^-?\d+$/', $this->value)) {
|
||||
$this->value = (int) $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +85,7 @@ class DBInt extends DBField
|
||||
return NumericField::create($this->name, $title);
|
||||
}
|
||||
|
||||
public function nullValue(): ?int
|
||||
public function nullValue(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
13
src/ORM/FieldType/DBIp.php
Normal file
13
src/ORM/FieldType/DBIp.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
use SilverStripe\Core\Validation\FieldValidation\IpFieldValidator;
|
||||
|
||||
class DBIp extends DBVarchar
|
||||
{
|
||||
private static array $field_validators = [
|
||||
IpFieldValidator::class,
|
||||
];
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Validation\FieldValidation\LocaleFieldValidator;
|
||||
use SilverStripe\i18n\i18n;
|
||||
|
||||
/**
|
||||
@ -9,6 +10,10 @@ use SilverStripe\i18n\i18n;
|
||||
*/
|
||||
class DBLocale extends DBVarchar
|
||||
{
|
||||
private static array $field_validators = [
|
||||
LocaleFieldValidator::class,
|
||||
];
|
||||
|
||||
public function __construct(?string $name = null, int $size = 16)
|
||||
{
|
||||
parent::__construct($name, $size);
|
||||
|
@ -3,6 +3,9 @@
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\MultiEnumFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
use SilverStripe\Forms\CheckboxSetField;
|
||||
use SilverStripe\Forms\MultiSelectField;
|
||||
use SilverStripe\ORM\Connect\MySQLDatabase;
|
||||
@ -13,6 +16,14 @@ use SilverStripe\ORM\DB;
|
||||
*/
|
||||
class DBMultiEnum extends DBEnum
|
||||
{
|
||||
private static array $field_validators = [
|
||||
// disable parent field validators
|
||||
StringFieldValidator::class => null,
|
||||
EnumFieldValidator::class => null,
|
||||
// enable multi enum field validator
|
||||
MultiEnumFieldValidator::class => ['getEnum'],
|
||||
];
|
||||
|
||||
public function __construct($name = null, $enum = null, $default = null)
|
||||
{
|
||||
// MultiEnum needs to take care of its own defaults
|
||||
@ -34,6 +45,15 @@ class DBMultiEnum extends DBEnum
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForValidation(): array
|
||||
{
|
||||
$value = parent::getValueForValidation();
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return explode(',', (string) $value);
|
||||
}
|
||||
|
||||
public function requireField(): void
|
||||
{
|
||||
$charset = Config::inst()->get(MySQLDatabase::class, 'charset');
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DecimalFieldValidator;
|
||||
|
||||
/**
|
||||
* Represents a decimal field from 0-1 containing a percentage value.
|
||||
@ -17,6 +18,10 @@ use SilverStripe\Model\ModelData;
|
||||
*/
|
||||
class DBPercentage extends DBDecimal
|
||||
{
|
||||
private static array $field_validators = [
|
||||
DecimalFieldValidator::class => ['getWholeSize', 'getDecimalSize', 'getMinValue', 'getMaxValue'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new Decimal field.
|
||||
*/
|
||||
@ -29,6 +34,16 @@ class DBPercentage extends DBDecimal
|
||||
parent::__construct($name, $precision + 1, $precision);
|
||||
}
|
||||
|
||||
public function getMinValue(): float
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public function getMaxValue(): float
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number, expressed as a percentage. For example, “36.30%”
|
||||
*/
|
||||
|
@ -5,12 +5,18 @@ namespace SilverStripe\ORM\FieldType;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\CompositeFieldValidator;
|
||||
|
||||
/**
|
||||
* A special ForeignKey class that handles relations with arbitrary class types
|
||||
*/
|
||||
class DBPolymorphicForeignKey extends DBComposite
|
||||
{
|
||||
private static array $field_validators = [
|
||||
// Disable parent field validator
|
||||
CompositeFieldValidator::class => null,
|
||||
];
|
||||
|
||||
private static bool $index = true;
|
||||
|
||||
private static array $composite_db = [
|
||||
|
@ -2,11 +2,18 @@
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
|
||||
/**
|
||||
* An abstract base class for the string field types (i.e. Varchar and Text)
|
||||
*/
|
||||
abstract class DBString extends DBField
|
||||
{
|
||||
private static array $field_validators = [
|
||||
StringFieldValidator::class,
|
||||
];
|
||||
|
||||
private static array $casting = [
|
||||
'LimitCharacters' => 'Text',
|
||||
'LimitCharactersToClosestWord' => 'Text',
|
||||
@ -17,13 +24,14 @@ abstract class DBString extends DBField
|
||||
];
|
||||
|
||||
/**
|
||||
* Set the default value for "nullify empty"
|
||||
* Set the default value for "nullify empty" and 'default'
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function __construct($name = null, $options = [])
|
||||
{
|
||||
$this->options['nullifyEmpty'] = true;
|
||||
$this->options['default'] = '';
|
||||
parent::__construct($name, $options);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\TimeFieldValidator;
|
||||
|
||||
/**
|
||||
* Represents a column in the database with the type 'Time'.
|
||||
@ -26,33 +27,30 @@ class DBTime extends DBField
|
||||
{
|
||||
/**
|
||||
* Standard ISO format string for time in CLDR standard format
|
||||
* This is equivalent to php date format "H:i:s" e.g. 09:30:00
|
||||
*/
|
||||
public const ISO_TIME = 'HH:mm:ss';
|
||||
|
||||
private static array $field_validators = [
|
||||
TimeFieldValidator::class,
|
||||
];
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
$value = $this->parseTime($value);
|
||||
if ($value === false) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid date passed. Use ' . $this->getISOFormat() . ' to prevent this error.'
|
||||
);
|
||||
}
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse timestamp or iso8601-ish date into standard iso8601 format
|
||||
*
|
||||
* @return string|null|false Formatted time, null if empty but valid, or false if invalid
|
||||
*/
|
||||
protected function parseTime(mixed $value): string|null|false
|
||||
protected function parseTime(mixed $value): mixed
|
||||
{
|
||||
// Skip empty values
|
||||
// Return empty dates as-is, they will get caught by validator later
|
||||
if (empty($value) && !is_numeric($value)) {
|
||||
return null;
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Determine value to parse
|
||||
if (is_array($value)) {
|
||||
$source = $value; // parse array
|
||||
@ -62,14 +60,16 @@ class DBTime extends DBField
|
||||
// Convert using strtotime
|
||||
$source = strtotime($value ?? '');
|
||||
}
|
||||
if ($value === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format as iso8601
|
||||
$formatter = $this->getFormatter();
|
||||
$formatter->setPattern($this->getISOFormat());
|
||||
return $formatter->format($source);
|
||||
$formatted = $formatter->format($source);
|
||||
// If it failed to format, return the original value so that it can
|
||||
// be caught by the validator
|
||||
if ($formatted === false) {
|
||||
return $value;
|
||||
}
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
|
22
src/ORM/FieldType/DBUrl.php
Normal file
22
src/ORM/FieldType/DBUrl.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\FieldType;
|
||||
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
use SilverStripe\Core\Validation\FieldValidation\UrlFieldValidator;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\Forms\UrlField;
|
||||
|
||||
class DBUrl extends DBVarchar
|
||||
{
|
||||
private static array $field_validators = [
|
||||
UrlFieldValidator::class,
|
||||
];
|
||||
|
||||
public function scaffoldFormField(?string $title = null, array $params = []): ?FormField
|
||||
{
|
||||
$field = UrlField::create($this->name, $title);
|
||||
$field->setMaxLength($this->getSize());
|
||||
return $field;
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ use SilverStripe\Forms\NullableField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\Connect\MySQLDatabase;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
|
||||
/**
|
||||
* Class Varchar represents a variable-length string of up to 255 characters, designed to store raw text
|
||||
@ -18,6 +19,10 @@ use SilverStripe\ORM\DB;
|
||||
*/
|
||||
class DBVarchar extends DBString
|
||||
{
|
||||
private static array $field_validators = [
|
||||
StringFieldValidator::class => [null, 'getSize'],
|
||||
];
|
||||
|
||||
private static array $casting = [
|
||||
'Initial' => 'Text',
|
||||
'URL' => 'Text',
|
||||
|
@ -5,12 +5,23 @@ namespace SilverStripe\ORM\FieldType;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Model\ModelData;
|
||||
use SilverStripe\Core\Validation\FieldValidation\YearFieldValidator;
|
||||
|
||||
/**
|
||||
* Represents a single year field.
|
||||
* Represents a single year field
|
||||
*/
|
||||
class DBYear extends DBField
|
||||
{
|
||||
// MySQL year datatype supports years between 1901 and 2155
|
||||
// https://dev.mysql.com/doc/refman/8.0/en/year.html
|
||||
public const MIN_YEAR = 1901;
|
||||
public const MAX_YEAR = 2155;
|
||||
|
||||
private static $field_validators = [
|
||||
YearFieldValidator::class => ['getMinYear', 'getMaxYear'],
|
||||
];
|
||||
|
||||
public function requireField(): void
|
||||
{
|
||||
$parts = ['datatype' => 'year', 'precision' => 4, 'arrayValue' => $this->arrayValue];
|
||||
@ -25,11 +36,51 @@ class DBYear extends DBField
|
||||
return $selectBox;
|
||||
}
|
||||
|
||||
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
|
||||
{
|
||||
parent::setValue($value, $record, $markChanged);
|
||||
// 0 is used to represent a null value, which will be stored as 0000 in MySQL
|
||||
if ($this->value === '0000') {
|
||||
$this->value = 0;
|
||||
}
|
||||
// shorthand for 2000 in MySQL
|
||||
if ($this->value === '00') {
|
||||
$this->value = 2000;
|
||||
}
|
||||
// convert string int to int
|
||||
// string int and int are both valid in MySQL, though only use int internally
|
||||
if (is_string($this->value) && preg_match('#^\d+$#', (string) $this->value)) {
|
||||
$this->value = (int) $this->value;
|
||||
}
|
||||
if (!is_int($this->value)) {
|
||||
return $this;
|
||||
}
|
||||
// shorthand for 2001-2069 in MySQL
|
||||
if ($this->value >= 1 && $this->value <= 69) {
|
||||
$this->value = 2000 + $this->value;
|
||||
}
|
||||
// shorthand for 1970-1999 in MySQL
|
||||
if ($this->value >= 70 && $this->value <= 99) {
|
||||
$this->value = 1900 + $this->value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMinYear(): int
|
||||
{
|
||||
return DBYear::MIN_YEAR;
|
||||
}
|
||||
|
||||
public function getMaxYear(): int
|
||||
{
|
||||
return DBYear::MAX_YEAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of default options that can
|
||||
* be used to populate a select box, or compare against
|
||||
* input values. Starts by default at the current year,
|
||||
* and counts back to 1900.
|
||||
* and counts back to 1901.
|
||||
*
|
||||
* @param int|null $start starting date to count down from
|
||||
* @param int|null $end end date to count down to
|
||||
@ -37,10 +88,10 @@ class DBYear extends DBField
|
||||
private function getDefaultOptions(?int $start = null, ?int $end = null): array
|
||||
{
|
||||
if (!$start) {
|
||||
$start = (int)date('Y');
|
||||
$start = (int) date('Y');
|
||||
}
|
||||
if (!$end) {
|
||||
$end = 1900;
|
||||
$end = DBYear::MIN_YEAR;
|
||||
}
|
||||
$years = [];
|
||||
for ($i = $start; $i >= $end; $i--) {
|
||||
|
@ -444,8 +444,8 @@ class Member extends DataObject
|
||||
if (!$this->PasswordExpiry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtotime(date('Y-m-d')) >= strtotime($this->PasswordExpiry ?? '');
|
||||
$expired = strtotime(date('Y-m-d')) >= strtotime($this->PasswordExpiry ?? '');
|
||||
return $expired;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1691,7 +1691,7 @@ class Member extends DataObject
|
||||
|
||||
// Overwrite the Password property with the hashed value
|
||||
$this->Password = $encryption_details['password'];
|
||||
$this->Salt = $encryption_details['salt'];
|
||||
$this->Salt = $encryption_details['salt'] ?: '';
|
||||
$this->PasswordEncryption = $encryption_details['algorithm'];
|
||||
|
||||
// If we haven't manually set a password expiry
|
||||
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\BigIntFieldValidator;
|
||||
|
||||
class BigIntFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-int' => [
|
||||
'value' => 123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-zero' => [
|
||||
'value' => 0,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative-int' => [
|
||||
'value' => -123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max-int' => [
|
||||
'value' => 9223372036854775807,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-min-int' => [
|
||||
'value' => '-9223372036854775808',
|
||||
'expected' => true,
|
||||
],
|
||||
// Note: cannot test out of range values as they casting them to int
|
||||
// will change the value to PHP_INT_MIN/PHP_INT_MAX
|
||||
'invalid-string-int' => [
|
||||
'value' => '123',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-float' => [
|
||||
'value' => 123.45,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => [123],
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-true' => [
|
||||
'value' => true,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
// On 64-bit systems, -9223372036854775808 will end up as a float
|
||||
// however it works correctly when cast to an int
|
||||
if ($value === '-9223372036854775808') {
|
||||
$value = (int) $value;
|
||||
}
|
||||
$validator = new BigIntFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\BooleanFieldValidator;
|
||||
|
||||
class BooleanFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-true' => [
|
||||
'value' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-false' => [
|
||||
'value' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-int-1' => [
|
||||
'value' => 1,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-int-0' => [
|
||||
'value' => 0,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-1' => [
|
||||
'value' => '1',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-0' => [
|
||||
'value' => '0',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-true' => [
|
||||
'value' => 'true',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-false' => [
|
||||
'value' => 'false',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string' => [
|
||||
'value' => 'abc',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-int' => [
|
||||
'value' => 123,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => [],
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new BooleanFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use stdClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Core\Validation\FieldValidation\CompositeFieldValidator;
|
||||
use SilverStripe\ORM\FieldType\DBBoolean;
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
|
||||
class CompositeFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'valueBoolean' => true,
|
||||
'valueString' => 'fish',
|
||||
'valueIsNull' => false,
|
||||
'exception' => null,
|
||||
'expected' => true,
|
||||
],
|
||||
'exception-not-iterable' => [
|
||||
'valueBoolean' => true,
|
||||
'valueString' => 'not-iterable',
|
||||
'valueIsNull' => false,
|
||||
'exception' => InvalidArgumentException::class,
|
||||
'expected' => true,
|
||||
],
|
||||
'exception-not-field-validator' => [
|
||||
'valueBoolean' => true,
|
||||
'valueString' => 'no-field-validation',
|
||||
'valueIsNull' => false,
|
||||
'exception' => InvalidArgumentException::class,
|
||||
'expected' => true,
|
||||
],
|
||||
'exception-do-not-skip-null' => [
|
||||
'valueBoolean' => true,
|
||||
'valueString' => 'fish',
|
||||
'valueIsNull' => true,
|
||||
'exception' => InvalidArgumentException::class,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-bool-field' => [
|
||||
'valueBoolean' => 'dog',
|
||||
'valueString' => 'fish',
|
||||
'valueIsNull' => false,
|
||||
'exception' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-field' => [
|
||||
'valueBoolean' => true,
|
||||
'valueString' => 456.789,
|
||||
'valueIsNull' => false,
|
||||
'exception' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(
|
||||
mixed $valueBoolean,
|
||||
mixed $valueString,
|
||||
bool $valueIsNull,
|
||||
?string $exception,
|
||||
bool $expected
|
||||
): void {
|
||||
if ($exception) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
if ($valueIsNull) {
|
||||
$iterable = null;
|
||||
} else {
|
||||
$booleanField = new DBBoolean('BooleanField');
|
||||
$booleanField->setValue($valueBoolean);
|
||||
if ($exception && $valueString === 'no-field-validation') {
|
||||
$stringField = new stdClass();
|
||||
} else {
|
||||
$stringField = new DBVarchar('StringField');
|
||||
$stringField->setValue($valueString);
|
||||
}
|
||||
if ($exception && $valueString === 'not-iterable') {
|
||||
$iterable = 'banana';
|
||||
} else {
|
||||
$iterable = [$booleanField, $stringField];
|
||||
}
|
||||
}
|
||||
$validator = new CompositeFieldValidator('MyField', $iterable);
|
||||
$result = $validator->validate();
|
||||
if (!$exception) {
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
|
||||
class DateFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => '2020-09-15',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-blank-string' => [
|
||||
'value' => '',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => '2020-02-30',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-wrong-format' => [
|
||||
'value' => '15-09-2020',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-date-time' => [
|
||||
'value' => '2020-09-15 13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-time' => [
|
||||
'value' => '13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new DateFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DatetimeFieldValidator;
|
||||
|
||||
class DatetimeFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => '2020-09-15 13:34:56',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-date' => [
|
||||
'value' => '2020-02-30 13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-time' => [
|
||||
'value' => '2020-02-15 13:99:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-wrong-format' => [
|
||||
'value' => '15-09-2020 13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-date-only' => [
|
||||
'value' => '2020-09-15',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-time-only' => [
|
||||
'value' => '13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new DatetimeFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DecimalFieldValidator;
|
||||
|
||||
class DecimalFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => 123.45,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative' => [
|
||||
'value' => -123.45,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-zero' => [
|
||||
'value' => 0,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-rounded-dp' => [
|
||||
'value' => 123.456,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-rounded-up' => [
|
||||
'value' => 123.999,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-int' => [
|
||||
'value' => 123,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative-int' => [
|
||||
'value' => -123,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max' => [
|
||||
'value' => 999.99,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max-negative' => [
|
||||
'value' => -999.99,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-rounded-to-6-digts' => [
|
||||
'value' => 999.999,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-too-long' => [
|
||||
'value' => 1234.56,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-too-long-3dp' => [
|
||||
'value' => 123.456,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 3,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-too-long-1dp' => [
|
||||
'value' => 123.4,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 3,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-too-long-int' => [
|
||||
'value' => 123,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 3,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string' => [
|
||||
'value' => '123.45',
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-true' => [
|
||||
'value' => true,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-false' => [
|
||||
'value' => false,
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => [123.45],
|
||||
'wholeSize' => 5,
|
||||
'decimalSize' => 2,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, int $wholeSize, int $decimalSize, bool $expected): void
|
||||
{
|
||||
$validator = new DecimalFieldValidator('MyField', $value, $wholeSize, $decimalSize);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EmailFieldValidator;
|
||||
|
||||
class EmailFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
// Using symfony/validator for implementation so only smoke testing
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => 'test@example.com',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => 'fish',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new EmailFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
|
||||
class EnumFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-string' => [
|
||||
'value' => 'cat',
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-int' => [
|
||||
'value' => 123,
|
||||
'allowedValues' => [123, 456],
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-none' => [
|
||||
'value' => '',
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => 'fish',
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-strict' => [
|
||||
'value' => '123',
|
||||
'allowedValues' => [123, 456],
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, array $allowedValues, bool $expected): void
|
||||
{
|
||||
$validator = new EnumFieldValidator('MyField', $value, $allowedValues);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
|
||||
|
||||
class IntFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-int' => [
|
||||
'value' => 123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-zero' => [
|
||||
'value' => 0,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative-int' => [
|
||||
'value' => -123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max-int' => [
|
||||
'value' => 2147483647,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-min-int' => [
|
||||
'value' => -2147483648,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-out-of-bounds' => [
|
||||
'value' => 2147483648,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-out-of-negative-bounds' => [
|
||||
'value' => -2147483649,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-string-int' => [
|
||||
'value' => '123',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-float' => [
|
||||
'value' => 123.45,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => [123],
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-true' => [
|
||||
'value' => true,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new IntFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\IpFieldValidator;
|
||||
|
||||
class IpFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
// Using symfony/validator for implementation so only smoke testing
|
||||
return [
|
||||
'valid-ipv4' => [
|
||||
'value' => '127.0.0.1',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-ipv6' => [
|
||||
'value' => '0:0:0:0:0:0:0:1',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-ipv6-short' => [
|
||||
'value' => '::1',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => '12345',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new IpFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\LocaleFieldValidator;
|
||||
|
||||
class LocaleFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
// Using symfony/validator for implementation so only smoke testing
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => 'de_DE',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-dash' => [
|
||||
'value' => 'de-DE',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-short' => [
|
||||
'value' => 'de',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => 'zz_ZZ',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-dash' => [
|
||||
'value' => 'zz-ZZ',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-short' => [
|
||||
'value' => 'zz',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-dashes' => [
|
||||
'value' => '-----',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-donut' => [
|
||||
'value' => 'donut',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new LocaleFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\MultiEnumFieldValidator;
|
||||
|
||||
class MultiEnumFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-string' => [
|
||||
'value' => ['cat'],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-multi-string' => [
|
||||
'value' => ['cat', 'dog'],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-none' => [
|
||||
'value' => [],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-int' => [
|
||||
'value' => [123],
|
||||
'allowedValues' => [123, 456],
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'exception-not-array' => [
|
||||
'value' => 'cat,dog',
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => true,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => ['fish'],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => [null],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-multi' => [
|
||||
'value' => ['dog', 'fish'],
|
||||
'allowedValues' => ['cat', 'dog'],
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-strict' => [
|
||||
'value' => ['123'],
|
||||
'allowedValues' => [123, 456],
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, array $allowedValues, bool $exception, bool $expected): void
|
||||
{
|
||||
if ($exception) {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
}
|
||||
$validator = new MultiEnumFieldValidator('MyField', $value, $allowedValues);
|
||||
$result = $validator->validate();
|
||||
if (!$exception) {
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\NumericFieldValidator;
|
||||
|
||||
class NumericFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-int' => [
|
||||
'value' => 123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-zero' => [
|
||||
'value' => 0,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative-int' => [
|
||||
'value' => -123,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-float' => [
|
||||
'value' => 123.45,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-negative-float' => [
|
||||
'value' => -123.45,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max-int' => [
|
||||
'value' => PHP_INT_MAX,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-min-int' => [
|
||||
'value' => PHP_INT_MIN,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max-float' => [
|
||||
'value' => PHP_FLOAT_MAX,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-min-float' => [
|
||||
'value' => PHP_FLOAT_MIN,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-string' => [
|
||||
'value' => '123',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => [123],
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-true' => [
|
||||
'value' => true,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new NumericFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class StringFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid-no-limit' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-blank' => [
|
||||
'value' => '',
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-max' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => 0,
|
||||
'maxLength' => 4,
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-less-than-max-null-min' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => null,
|
||||
'maxLength' => 4,
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-less-than-max-unicode' => [
|
||||
'value' => '☕☕☕☕',
|
||||
'minLength' => 0,
|
||||
'maxLength' => 4,
|
||||
'exception' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'exception-negative-min' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => -1,
|
||||
'maxLength' => null,
|
||||
'exception' => true,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-below-min' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => 5,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-below-min-unicode' => [
|
||||
'value' => '☕☕☕☕',
|
||||
'minLength' => 5,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-blank-with-min' => [
|
||||
'value' => '',
|
||||
'minLength' => 5,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-above-min' => [
|
||||
'value' => 'fish',
|
||||
'minLength' => 0,
|
||||
'maxLength' => 3,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-above-min-unicode' => [
|
||||
'value' => '☕☕☕☕',
|
||||
'minLength' => 0,
|
||||
'maxLength' => 3,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-int' => [
|
||||
'value' => 123,
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-float' => [
|
||||
'value' => 123.56,
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-true' => [
|
||||
'value' => true,
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-false' => [
|
||||
'value' => false,
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-array' => [
|
||||
'value' => ['fish'],
|
||||
'minLength' => null,
|
||||
'maxLength' => null,
|
||||
'exception' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, ?int $minLength, ?int $maxLength, bool $exception, bool $expected): void
|
||||
{
|
||||
if ($exception) {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
}
|
||||
$validator = new StringFieldValidator('MyField', $value, $minLength, $maxLength);
|
||||
$result = $validator->validate();
|
||||
if (!$exception) {
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\TimeFieldValidator;
|
||||
|
||||
class TimeFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => '13:34:56',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => '13:99:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-wrong-format' => [
|
||||
'value' => '13-34-56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-date-time' => [
|
||||
'value' => '2020-09-15 13:34:56',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-date' => [
|
||||
'value' => '2020-09-15',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new TimeFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\UrlFieldValidator;
|
||||
|
||||
class UrlFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
// Using symfony/validator for implementation so only smoke testing
|
||||
return [
|
||||
'valid-https' => [
|
||||
'value' => 'https://www.example.com',
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-http' => [
|
||||
'value' => 'https://www.example.com',
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-ftp' => [
|
||||
'value' => 'ftp://www.example.com',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-no-scheme' => [
|
||||
'value' => 'www.example.com',
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-null' => [
|
||||
'value' => null,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new UrlFieldValidator('MyField', $value);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\FieldValidation\YearFieldValidator;
|
||||
use SilverStripe\ORM\FieldType\DBYear;
|
||||
|
||||
class YearFieldValidatorTest extends SapphireTest
|
||||
{
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
// YearFieldValidator extends IntFieldValidator so only testing a subset
|
||||
// of possible values here
|
||||
return [
|
||||
'valid-int' => [
|
||||
'value' => 2021,
|
||||
'expected' => true,
|
||||
],
|
||||
'valid-zero' => [
|
||||
'value' => 0,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid-out-of-range-low' => [
|
||||
'value' => 1850,
|
||||
'expected' => false,
|
||||
],
|
||||
'invalid-out-of-range-high' => [
|
||||
'value' => 3000,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$validator = new YearFieldValidator('MyField', $value, DBYear::MIN_YEAR, DBYear::MAX_YEAR);
|
||||
$result = $validator->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ use SilverStripe\Security\PermissionCheckboxSetField_Readonly;
|
||||
use SilverStripe\Forms\SearchableMultiDropdownField;
|
||||
use SilverStripe\Forms\SearchableDropdownField;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\ORM\FieldType\DBInt;
|
||||
|
||||
class FormFieldTest extends SapphireTest
|
||||
{
|
||||
|
98
tests/php/ORM/DBBooleanTest.php
Normal file
98
tests/php/ORM/DBBooleanTest.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\ORM\FieldType\DBBoolean;
|
||||
|
||||
class DBBooleanTest extends SapphireTest
|
||||
{
|
||||
public function testDefaultValue(): void
|
||||
{
|
||||
$field = new DBBoolean('MyField');
|
||||
$this->assertSame(false, $field->getValue());
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'true' => [
|
||||
'value' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'1-int' => [
|
||||
'value' => 1,
|
||||
'expected' => true,
|
||||
],
|
||||
'1-string' => [
|
||||
'value' => '1',
|
||||
'expected' => true,
|
||||
],
|
||||
'0-int' => [
|
||||
'value' => 0,
|
||||
'expected' => false,
|
||||
],
|
||||
'0-string' => [
|
||||
'value' => '0',
|
||||
'expected' => false,
|
||||
],
|
||||
't' => [
|
||||
'value' => 't',
|
||||
'expected' => true,
|
||||
],
|
||||
'f' => [
|
||||
'value' => 'f',
|
||||
'expected' => false,
|
||||
],
|
||||
'T' => [
|
||||
'value' => 'T',
|
||||
'expected' => true,
|
||||
],
|
||||
'F' => [
|
||||
'value' => 'F',
|
||||
'expected' => false,
|
||||
],
|
||||
'true-string' => [
|
||||
'value' => 'true',
|
||||
'expected' => true,
|
||||
],
|
||||
'false-string' => [
|
||||
'value' => 'false',
|
||||
'expected' => false,
|
||||
],
|
||||
'2-int' => [
|
||||
'value' => 2,
|
||||
'expected' => 2,
|
||||
],
|
||||
'0.0' => [
|
||||
'value' => 0.0,
|
||||
'expected' => 0.0,
|
||||
],
|
||||
'1.0' => [
|
||||
'value' => 1.0,
|
||||
'expected' => 1.0,
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
'array' => [
|
||||
'value' => [],
|
||||
'expected' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBBoolean('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
@ -6,6 +6,9 @@ use SilverStripe\ORM\FieldType\DBMoney;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||
use SilverStripe\ORM\FieldType\DBDecimal;
|
||||
|
||||
class DBCompositeTest extends SapphireTest
|
||||
{
|
||||
@ -140,4 +143,12 @@ class DBCompositeTest extends SapphireTest
|
||||
// $this->assertSame($moneyField, $obj->dbObject('DoubleMoney'));
|
||||
// $this->assertEquals(20, $obj->dbObject('DoubleMoney')->getAmount());
|
||||
}
|
||||
|
||||
public function testGetValueForValidation(): void
|
||||
{
|
||||
$obj = DBCompositeTest\DBDoubleMoney::create();
|
||||
$expected = [DBVarchar::class, DBDecimal::class];
|
||||
$actual = array_map('get_class', $obj->getValueForValidation());
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\ORM\Tests;
|
||||
use SilverStripe\Forms\CurrencyField;
|
||||
use SilverStripe\ORM\FieldType\DBCurrency;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
class DBCurrencyTest extends SapphireTest
|
||||
{
|
||||
@ -15,11 +16,6 @@ class DBCurrencyTest extends SapphireTest
|
||||
// Test basic operation
|
||||
'$50.00' => ['$50.00', '$50'],
|
||||
|
||||
// Test removal of junk text
|
||||
'this is -50.29 dollars' => ['($50.29)', '($50)'],
|
||||
'this is -50.79 dollars' => ['($50.79)', '($51)'],
|
||||
'this is 50.79 dollars' => ['$50.79', '$51'],
|
||||
|
||||
// Test negative numbers
|
||||
'-1000' => ['($1,000.00)','($1,000)'],
|
||||
'-$2,000' => ['($2,000.00)', '($2,000)'],
|
||||
@ -30,9 +26,6 @@ class DBCurrencyTest extends SapphireTest
|
||||
// Test scientific notation
|
||||
'5.68434188608E-14' => ['$0.00', '$0'],
|
||||
'5.68434188608E7' => ['$56,843,418.86', '$56,843,419'],
|
||||
"Sometimes Es are still bad: 51 dollars, even though they\'re used in scientific notation"
|
||||
=> ['$51.00', '$51'],
|
||||
"What about 5.68434188608E7 in the middle of a string" => ['$56,843,418.86', '$56,843,419'],
|
||||
];
|
||||
|
||||
foreach ($tests as $value => $niceValues) {
|
||||
@ -51,4 +44,75 @@ class DBCurrencyTest extends SapphireTest
|
||||
|
||||
$this->assertInstanceOf(CurrencyField::class, $scaffoldedField);
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
// Most test cases covered by DBCurrencyTest, only testing a subset here
|
||||
return [
|
||||
'currency' => [
|
||||
'value' => '$1.23',
|
||||
'expected' => 1.23,
|
||||
],
|
||||
'negative-currency' => [
|
||||
'value' => "-$1.23",
|
||||
'expected' => -1.23,
|
||||
],
|
||||
'scientific-1' => [
|
||||
'value' => 5.68434188608E-14,
|
||||
'expected' => 5.68434188608E-14,
|
||||
],
|
||||
'scientific-2' => [
|
||||
'value' => 5.68434188608E7,
|
||||
'expected' => 56843418.8608,
|
||||
],
|
||||
'scientific-1-string' => [
|
||||
'value' => '5.68434188608E-14',
|
||||
'expected' => 5.68434188608E-14,
|
||||
],
|
||||
'scientific-2-string' => [
|
||||
'value' => '5.68434188608E7',
|
||||
'expected' => 56843418.8608,
|
||||
],
|
||||
'int' => [
|
||||
'value' => 1,
|
||||
'expected' => 1.0,
|
||||
],
|
||||
'string-int' => [
|
||||
'value' => "1",
|
||||
'expected' => 1.0,
|
||||
],
|
||||
'string-float' => [
|
||||
'value' => '1.2',
|
||||
'expected' => 1.2,
|
||||
],
|
||||
'value-in-string' => [
|
||||
'value' => 'this is 50.29 dollars',
|
||||
'expected' => 'this is 50.29 dollars',
|
||||
],
|
||||
'scientific-value-in-string' => [
|
||||
'value' => '5.68434188608E7 a string',
|
||||
'expected' => '5.68434188608E7 a string',
|
||||
],
|
||||
'value-in-brackets' => [
|
||||
'value' => '(100)',
|
||||
'expected' => '(100)',
|
||||
],
|
||||
'non-numeric' => [
|
||||
'value' => 'fish',
|
||||
'expected' => 'fish',
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBCurrency('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ use SilverStripe\ORM\FieldType\DBDate;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\Validation\ValidationException;
|
||||
|
||||
class DBDateTest extends SapphireTest
|
||||
{
|
||||
@ -91,20 +92,6 @@ class DBDateTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testMDYConversion()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage("Invalid date: '3/16/2003'. Use y-MM-dd to prevent this error.");
|
||||
DBField::create_field('Date', '3/16/2003');
|
||||
}
|
||||
|
||||
public function testY2kCorrection()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage("Invalid date: '03-03-04'. Use y-MM-dd to prevent this error.");
|
||||
DBField::create_field('Date', '03-03-04');
|
||||
}
|
||||
|
||||
public function testInvertedYearCorrection()
|
||||
{
|
||||
// iso8601 expects year first, but support year last
|
||||
@ -194,31 +181,70 @@ class DBDateTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetNullAndZeroValues()
|
||||
public static function provideSetValue()
|
||||
{
|
||||
$date = DBField::create_field('Date', '');
|
||||
$this->assertNull($date->getValue(), 'Empty string evaluates to NULL');
|
||||
return [
|
||||
'date' => [
|
||||
'value' => '1998-04-05',
|
||||
'expected' => '1998-04-05'
|
||||
],
|
||||
'int' => [
|
||||
'value' => 978797878,
|
||||
'expected' => '2001-01-06'
|
||||
],
|
||||
'int-string' => [
|
||||
'value' => '978797878',
|
||||
'expected' => '2001-01-06'
|
||||
],
|
||||
'blank-string' => [
|
||||
'value' => '',
|
||||
'expected' => ''
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false
|
||||
],
|
||||
'empty-array' => [
|
||||
'value' => [],
|
||||
'expected' => []
|
||||
],
|
||||
'zero-string' => [
|
||||
'value' => '0',
|
||||
'expected' => '1970-01-01'
|
||||
],
|
||||
'zero-int' => [
|
||||
'value' => 0,
|
||||
'expected' => '1970-01-01'
|
||||
],
|
||||
'zero-date' => [
|
||||
'value' => '0000-00-00',
|
||||
'expected' => '0000-00-00'
|
||||
],
|
||||
'zero-date-slashes' => [
|
||||
'value' => '00/00/0000',
|
||||
'expected' => '00/00/0000'
|
||||
],
|
||||
'wrong-format-a' => [
|
||||
'value' => '3/16/2003',
|
||||
'expected' => '3/16/2003',
|
||||
],
|
||||
'wrong-format-b' => [
|
||||
'value' => '03-03-04',
|
||||
'expected' => '2003-03-04',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$date = DBField::create_field('Date', null);
|
||||
$this->assertNull($date->getValue(), 'NULL is set as NULL');
|
||||
|
||||
$date = DBField::create_field('Date', false);
|
||||
$this->assertNull($date->getValue(), 'Boolean FALSE evaluates to NULL');
|
||||
|
||||
$date = DBField::create_field('Date', []);
|
||||
$this->assertNull($date->getValue(), 'Empty array evaluates to NULL');
|
||||
|
||||
$date = DBField::create_field('Date', '0');
|
||||
$this->assertEquals('1970-01-01', $date->getValue(), 'Zero is UNIX epoch date');
|
||||
|
||||
$date = DBField::create_field('Date', 0);
|
||||
$this->assertEquals('1970-01-01', $date->getValue(), 'Zero is UNIX epoch date');
|
||||
|
||||
$date = DBField::create_field('Date', '0000-00-00 00:00:00');
|
||||
$this->assertNull($date->getValue(), '0000-00-00 00:00:00 is set as NULL');
|
||||
|
||||
$date = DBField::create_field('Date', '00/00/0000');
|
||||
$this->assertNull($date->getValue(), '00/00/0000 is set as NULL');
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected)
|
||||
{
|
||||
$field = new DBDate('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
|
||||
public function testDayOfMonth()
|
||||
|
@ -79,24 +79,6 @@ class DBDatetimeTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetNullAndZeroValues()
|
||||
{
|
||||
$date = DBDatetime::create_field('Datetime', '');
|
||||
$this->assertNull($date->getValue(), 'Empty string evaluates to NULL');
|
||||
|
||||
$date = DBDatetime::create_field('Datetime', null);
|
||||
$this->assertNull($date->getValue(), 'NULL is set as NULL');
|
||||
|
||||
$date = DBDatetime::create_field('Datetime', false);
|
||||
$this->assertNull($date->getValue(), 'Boolean FALSE evaluates to NULL');
|
||||
|
||||
$date = DBDatetime::create_field('Datetime', '0');
|
||||
$this->assertEquals('1970-01-01 00:00:00', $date->getValue(), 'String zero is UNIX epoch time');
|
||||
|
||||
$date = DBDatetime::create_field('Datetime', 0);
|
||||
$this->assertEquals('1970-01-01 00:00:00', $date->getValue(), 'Numeric zero is UNIX epoch time');
|
||||
}
|
||||
|
||||
public function testExtendedDateTimes()
|
||||
{
|
||||
$date = DBDatetime::create_field('Datetime', '1600-10-10 15:32:24');
|
||||
@ -348,4 +330,50 @@ class DBDatetimeTest extends SapphireTest
|
||||
$after = (new DBDateTime())->setValue($timeAfter);
|
||||
$this->assertSame($expected, DBDatetime::getTimeBetween($before, $after));
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'int' => [
|
||||
'value' => 800001234,
|
||||
'expected' => '1995-05-09 06:33:54',
|
||||
],
|
||||
'string-int' => [
|
||||
'value' => '800001234',
|
||||
'expected' => '1995-05-09 06:33:54',
|
||||
],
|
||||
'zero' => [
|
||||
'value' => 0,
|
||||
'expected' => '1970-01-01 00:00:00',
|
||||
],
|
||||
'zeroed' => [
|
||||
'value' => '0000-00-00 00:00:00',
|
||||
'expected' => '0000-00-00 00:00:00',
|
||||
],
|
||||
'non-date-string' => [
|
||||
'value' => 'fish',
|
||||
'expected' => 'fish',
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
'true' => [
|
||||
'value' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBDatetime('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
||||
|
87
tests/php/ORM/DBDecimalTest.php
Normal file
87
tests/php/ORM/DBDecimalTest.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBInt;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\ORM\FieldType\DBDecimal;
|
||||
|
||||
class DBDecimalTest extends SapphireTest
|
||||
{
|
||||
public function testDefaultValue(): void
|
||||
{
|
||||
$field = new DBDecimal('MyField');
|
||||
$this->assertSame(0.0, $field->getValue());
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'float' => [
|
||||
'value' => 9.123,
|
||||
'expected' => 9.123,
|
||||
],
|
||||
'negative-float' => [
|
||||
'value' => -9.123,
|
||||
'expected' => -9.123,
|
||||
],
|
||||
'string-float' => [
|
||||
'value' => '9.123',
|
||||
'expected' => 9.123,
|
||||
],
|
||||
'string-negative-float' => [
|
||||
'value' => '-9.123',
|
||||
'expected' => -9.123,
|
||||
],
|
||||
'zero' => [
|
||||
'value' => 0,
|
||||
'expected' => 0.0,
|
||||
],
|
||||
'int' => [
|
||||
'value' => 3,
|
||||
'expected' => 3.0,
|
||||
],
|
||||
'negative-int' => [
|
||||
'value' => -3,
|
||||
'expected' => -3.0,
|
||||
],
|
||||
'string-int' => [
|
||||
'value' => '3',
|
||||
'expected' => 3.0,
|
||||
],
|
||||
'negative-string-int' => [
|
||||
'value' => '-3',
|
||||
'expected' => -3.0,
|
||||
],
|
||||
'string' => [
|
||||
'value' => 'fish',
|
||||
'expected' => 'fish',
|
||||
],
|
||||
'array' => [
|
||||
'value' => [],
|
||||
'expected' => [],
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
'true' => [
|
||||
'value' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBDecimal('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
@ -118,7 +118,7 @@ class DBEnumTest extends SapphireTest
|
||||
|
||||
$obj2 = new FieldType\DBEnumTestObject();
|
||||
$obj2->Colour = 'Purple';
|
||||
$obj2->write();
|
||||
$obj2->write(skipValidation: true);
|
||||
|
||||
$this->assertEquals(
|
||||
['Purple', 'Red'],
|
||||
@ -141,4 +141,48 @@ class DBEnumTest extends SapphireTest
|
||||
$colourField->getEnumObsolete()
|
||||
);
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'string' => [
|
||||
'value' => 'green',
|
||||
'expected' => 'green',
|
||||
],
|
||||
'string-not-in-set' => [
|
||||
'value' => 'purple',
|
||||
'expected' => 'purple',
|
||||
],
|
||||
'int' => [
|
||||
'value' => 123,
|
||||
'expected' => 123,
|
||||
],
|
||||
'empty-string' => [
|
||||
'value' => '',
|
||||
'expected' => '',
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBEnum('TestField', ['red', 'green', 'blue'], 'blue');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
|
||||
public function testSaveDefaultValue()
|
||||
{
|
||||
$obj = new FieldType\DBEnumTestObject();
|
||||
$obj->Colour = null;
|
||||
$id = $obj->write();
|
||||
// Fetch the object from the database
|
||||
$colour = FieldType\DBEnumTestObject::get()->byID($id)->Colour;
|
||||
$this->assertEquals('Red', $colour);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Assets\Image;
|
||||
use Exception;
|
||||
use SilverStripe\ORM\FieldType\DBBigInt;
|
||||
use SilverStripe\ORM\FieldType\DBBoolean;
|
||||
use SilverStripe\ORM\FieldType\DBCurrency;
|
||||
@ -30,6 +30,32 @@ use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\ORM\FieldType\DBYear;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use ReflectionClass;
|
||||
use SilverStripe\Core\Validation\FieldValidation\BooleanFieldValidator;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Core\Validation\FieldValidation\BigIntFieldValidator;
|
||||
use SilverStripe\ORM\FieldType\DBClassName;
|
||||
use ReflectionMethod;
|
||||
use SilverStripe\Core\Validation\FieldValidation\CompositeFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DateFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DecimalFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EmailFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\EnumFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\IpFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\LocaleFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\MultiEnumFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\StringFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\TimeFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\UrlFieldValidator;
|
||||
use SilverStripe\Core\Validation\FieldValidation\YearFieldValidator;
|
||||
use SilverStripe\ORM\FieldType\DBUrl;
|
||||
use SilverStripe\ORM\FieldType\DBPolymorphicRelationAwareForeignKey;
|
||||
use SilverStripe\ORM\FieldType\DBIp;
|
||||
use SilverStripe\ORM\FieldType\DBEmail;
|
||||
use SilverStripe\Core\Validation\FieldValidation\DatetimeFieldValidator;
|
||||
use SilverStripe\ORM\FieldType\DBClassNameVarchar;
|
||||
|
||||
/**
|
||||
* Tests for DBField objects.
|
||||
@ -392,4 +418,179 @@ class DBFieldTest extends SapphireTest
|
||||
|
||||
$this->assertEquals('new value', $obj->getField('MyTestField'));
|
||||
}
|
||||
|
||||
public function testDefaultValues(): void
|
||||
{
|
||||
$expectedBaseDefault = null;
|
||||
$expectedDefaults = [
|
||||
DBBoolean::class => false,
|
||||
DBDecimal::class => 0.0,
|
||||
DBInt::class => 0,
|
||||
DBFloat::class => 0.0,
|
||||
];
|
||||
$count = 0;
|
||||
$classes = ClassInfo::subclassesFor(DBField::class);
|
||||
foreach ($classes as $class) {
|
||||
if (is_a($class, TestOnly::class, true)) {
|
||||
continue;
|
||||
}
|
||||
if (!str_starts_with($class, 'SilverStripe\ORM\FieldType')) {
|
||||
continue;
|
||||
}
|
||||
$reflector = new ReflectionClass($class);
|
||||
if ($reflector->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
$expected = $expectedBaseDefault;
|
||||
foreach ($expectedDefaults as $baseClass => $default) {
|
||||
if ($class === $baseClass || is_subclass_of($class, $baseClass)) {
|
||||
$expected = $default;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$field = new $class('TestField');
|
||||
$this->assertSame($expected, $field->getValue(), $class);
|
||||
$count++;
|
||||
}
|
||||
// Assert that we have tested all classes e.g. namespace wasn't changed, no new classes were added
|
||||
// that haven't been tested
|
||||
$this->assertSame(29, $count);
|
||||
}
|
||||
|
||||
public function testFieldValidatorConfig(): void
|
||||
{
|
||||
$expectedFieldValidators = [
|
||||
DBBigInt::class => [
|
||||
BigIntFieldValidator::class,
|
||||
],
|
||||
DBBoolean::class => [
|
||||
BooleanFieldValidator::class,
|
||||
],
|
||||
DBClassName::class => [
|
||||
StringFieldValidator::class,
|
||||
EnumFieldValidator::class,
|
||||
],
|
||||
DBClassNameVarchar::class => [
|
||||
StringFieldValidator::class,
|
||||
EnumFieldValidator::class,
|
||||
],
|
||||
DBCurrency::class => [
|
||||
DecimalFieldValidator::class,
|
||||
],
|
||||
DBDate::class => [
|
||||
DateFieldValidator::class,
|
||||
],
|
||||
DBDatetime::class => [
|
||||
DatetimeFieldValidator::class,
|
||||
],
|
||||
DBDecimal::class => [
|
||||
DecimalFieldValidator::class,
|
||||
],
|
||||
DBDouble::class => [],
|
||||
DBEmail::class => [
|
||||
StringFieldValidator::class,
|
||||
EmailFieldValidator::class,
|
||||
],
|
||||
DBEnum::class => [
|
||||
StringFieldValidator::class,
|
||||
EnumFieldValidator::class,
|
||||
],
|
||||
DBFloat::class => [],
|
||||
DBForeignKey::class => [
|
||||
IntFieldValidator::class,
|
||||
],
|
||||
DBHTMLText::class => [
|
||||
StringFieldValidator::class,
|
||||
],
|
||||
DBHTMLVarchar::class => [
|
||||
StringFieldValidator::class,
|
||||
],
|
||||
DBInt::class => [
|
||||
IntFieldValidator::class,
|
||||
],
|
||||
DBIp::class => [
|
||||
StringFieldValidator::class,
|
||||
IpFieldValidator::class,
|
||||
],
|
||||
DBLocale::class => [
|
||||
StringFieldValidator::class,
|
||||
LocaleFieldValidator::class,
|
||||
],
|
||||
DBMoney::class => [
|
||||
CompositeFieldValidator::class,
|
||||
],
|
||||
DBMultiEnum::class => [
|
||||
MultiEnumFieldValidator::class,
|
||||
],
|
||||
DBPercentage::class => [
|
||||
DecimalFieldValidator::class,
|
||||
],
|
||||
DBPolymorphicForeignKey::class => [],
|
||||
DBPolymorphicRelationAwareForeignKey::class => [],
|
||||
DBPrimaryKey::class => [
|
||||
IntFieldValidator::class,
|
||||
],
|
||||
DBText::class => [
|
||||
StringFieldValidator::class,
|
||||
],
|
||||
DBTime::class => [
|
||||
TimeFieldValidator::class,
|
||||
],
|
||||
DBUrl::class => [
|
||||
StringFieldValidator::class,
|
||||
UrlFieldValidator::class,
|
||||
],
|
||||
DBVarchar::class => [
|
||||
StringFieldValidator::class,
|
||||
],
|
||||
DBYear::class => [
|
||||
YearFieldValidator::class,
|
||||
],
|
||||
];
|
||||
$count = 0;
|
||||
$classes = ClassInfo::subclassesFor(DBField::class);
|
||||
foreach ($classes as $class) {
|
||||
if (is_a($class, TestOnly::class, true)) {
|
||||
continue;
|
||||
}
|
||||
if (!str_starts_with($class, 'SilverStripe\ORM\FieldType')) {
|
||||
continue;
|
||||
}
|
||||
$reflector = new ReflectionClass($class);
|
||||
if ($reflector->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists($class, $expectedFieldValidators)) {
|
||||
throw new Exception("No field validator config found for $class");
|
||||
}
|
||||
$expected = $expectedFieldValidators[$class];
|
||||
$method = new ReflectionMethod($class, 'getFieldValidators');
|
||||
$method->setAccessible(true);
|
||||
$obj = new $class('MyField');
|
||||
$actual = array_map('get_class', $method->invoke($obj));
|
||||
$this->assertSame($expected, $actual, $class);
|
||||
$count++;
|
||||
}
|
||||
// Assert that we have tested all classes e.g. namespace wasn't changed, no new classes were added
|
||||
// that haven't been tested
|
||||
$this->assertSame(29, $count);
|
||||
}
|
||||
|
||||
public function testSkipValidateIfNull()
|
||||
{
|
||||
$field = new DBInt('MyField');
|
||||
$field->setValue(null);
|
||||
// assert value isn't getting changed on setValue();
|
||||
$this->assertNull($field->getValue());
|
||||
// assert that field validators were not called
|
||||
$this->assertTrue($field->validate()->isValid());
|
||||
// assert that IntFieldValidator was applied to the field
|
||||
$method = new ReflectionMethod(DBInt::class, 'getFieldValidators');
|
||||
$method->setAccessible(true);
|
||||
$actual = array_map('get_class', $method->invoke($field));
|
||||
$this->assertSame([IntFieldValidator::class], $actual);
|
||||
// assert that IntFieldValidator considers null as invalid
|
||||
$validator = new IntFieldValidator('Test', null);
|
||||
$this->assertFalse($validator->validate()->isValid());
|
||||
}
|
||||
}
|
||||
|
44
tests/php/ORM/DBForiegnKeyTest.php
Normal file
44
tests/php/ORM/DBForiegnKeyTest.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use SilverStripe\ORM\FieldType\DBForeignKey;
|
||||
|
||||
class DBForiegnKeyTest extends SapphireTest
|
||||
{
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'int' => [
|
||||
'value' => 2,
|
||||
'expected' => 2,
|
||||
],
|
||||
'string' => [
|
||||
'value' => '2',
|
||||
'expected' => 2,
|
||||
],
|
||||
'zero' => [
|
||||
'value' => 0,
|
||||
'expected' => 0,
|
||||
],
|
||||
'blank-string' => [
|
||||
'value' => '',
|
||||
'expected' => 0,
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBForeignKey('TestField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
@ -4,15 +4,82 @@ namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBInt;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
class DBIntTest extends SapphireTest
|
||||
{
|
||||
public function testGetValueCastToInt()
|
||||
public function testDefaultValue(): void
|
||||
{
|
||||
$field = DBInt::create('MyField');
|
||||
$field->setValue(3);
|
||||
$this->assertSame(3, $field->getValue());
|
||||
$field->setValue('3');
|
||||
$this->assertSame(3, $field->getValue());
|
||||
$field = new DBInt('MyField');
|
||||
$this->assertSame(0, $field->getValue());
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'int' => [
|
||||
'value' => 3,
|
||||
'expected' => 3,
|
||||
],
|
||||
'string-int' => [
|
||||
'value' => '3',
|
||||
'expected' => 3,
|
||||
],
|
||||
'negative-int' => [
|
||||
'value' => -3,
|
||||
'expected' => -3,
|
||||
],
|
||||
'negative-string-int' => [
|
||||
'value' => '-3',
|
||||
'expected' => -3,
|
||||
],
|
||||
'float' => [
|
||||
'value' => 3.5,
|
||||
'expected' => 3.5,
|
||||
],
|
||||
'string' => [
|
||||
'value' => 'fish',
|
||||
'expected' => 'fish',
|
||||
],
|
||||
'array' => [
|
||||
'value' => [],
|
||||
'expected' => [],
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBInt('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
|
||||
public static function provideValidate(): array
|
||||
{
|
||||
return [
|
||||
'valid' => [
|
||||
'value' => 123,
|
||||
'expected' => true,
|
||||
],
|
||||
'invalid' => [
|
||||
'value' => 'abc',
|
||||
'expected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideValidate')]
|
||||
public function testValidate(mixed $value, bool $expected): void
|
||||
{
|
||||
$field = new DBInt('MyField');
|
||||
$field->setValue($value);
|
||||
$result = $field->validate();
|
||||
$this->assertSame($expected, $result->isValid());
|
||||
}
|
||||
}
|
||||
|
44
tests/php/ORM/DBMultiEnumTest.php
Normal file
44
tests/php/ORM/DBMultiEnumTest.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\ORM\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBMultiEnum;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
class DBMultiEnumTest extends SapphireTest
|
||||
{
|
||||
public static function provideGetValueForValidation(): array
|
||||
{
|
||||
return [
|
||||
'array' => [
|
||||
'value' => ['Red', 'Green'],
|
||||
'expected' => ['Red', 'Green'],
|
||||
],
|
||||
'string' => [
|
||||
'value' => 'Red,Green',
|
||||
'expected' => ['Red', 'Green'],
|
||||
],
|
||||
'string-non-existant-value' => [
|
||||
'value' => 'Red,Green,Purple',
|
||||
'expected' => ['Red', 'Green', 'Purple'],
|
||||
],
|
||||
'empty-string' => [
|
||||
'value' => '',
|
||||
'expected' => [''],
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => [''],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideGetValueForValidation')]
|
||||
public function testGetValueForValidation(mixed $value, array $expected): void
|
||||
{
|
||||
$obj = new DBMultiEnum('TestField', ['Red', 'Green', 'Blue']);
|
||||
$obj->setValue($value);
|
||||
$this->assertSame($expected, $obj->getValueForValidation());
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\ORM\FieldType\DBString;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\Tests\DBStringTest\MyStringField;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
class DBStringTest extends SapphireTest
|
||||
{
|
||||
|
@ -17,33 +17,78 @@ class DBTimeTest extends SapphireTest
|
||||
i18n::set_locale('en_NZ');
|
||||
}
|
||||
|
||||
public static function dataTestParse()
|
||||
public static function provideSetValue()
|
||||
{
|
||||
return [
|
||||
// Test am-pm conversion
|
||||
['11:01 pm', '23:01:00'],
|
||||
['11:01 am', '11:01:00'],
|
||||
['12:01 pm', '12:01:00'],
|
||||
['12:01 am', '00:01:00'],
|
||||
// Test seconds
|
||||
['11:01.01 pm', '23:01:01'],
|
||||
['12:01.01', '12:01:01'],
|
||||
'time-11pm' => [
|
||||
'value' => '11:01 pm',
|
||||
'expected' => '23:01:00'
|
||||
],
|
||||
'time-11am' => [
|
||||
'value' => '11:01 am',
|
||||
'expected' => '11:01:00'
|
||||
],
|
||||
'time-12am' => [
|
||||
'value' => '12:01 am',
|
||||
'expected' => '00:01:00'
|
||||
],
|
||||
'time-12pm' => [
|
||||
'value' => '12:01 pm',
|
||||
'expected' => '12:01:00'
|
||||
],
|
||||
'time-11pm-seconds' => [
|
||||
'value' => '11:01.01 pm',
|
||||
'expected' => '23:01:01'
|
||||
],
|
||||
'time-12-seconds' => [
|
||||
'value' => '12:01.01',
|
||||
'expected' => '12:01:01'
|
||||
],
|
||||
'wrong-format-works' => [
|
||||
'value' => '12.34.56',
|
||||
'expected' => '12:34:56',
|
||||
],
|
||||
'int' => [
|
||||
'value' => 6789,
|
||||
'expected' => '01:53:09'
|
||||
],
|
||||
'int-string' => [
|
||||
'value' => '6789',
|
||||
'expected' => '01:53:09'
|
||||
],
|
||||
'zero-string' => [
|
||||
'value' => '0',
|
||||
'expected' => '00:00:00'
|
||||
],
|
||||
'zero-int' => [
|
||||
'value' => 0,
|
||||
'expected' => '00:00:00'
|
||||
],
|
||||
'blank-string' => [
|
||||
'value' => '',
|
||||
'expected' => ''
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false
|
||||
],
|
||||
'empty-array' => [
|
||||
'value' => [],
|
||||
'expected' => []
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
*/
|
||||
#[DataProvider('dataTestParse')]
|
||||
public function testParse($input, $expected)
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected)
|
||||
{
|
||||
$time = DBField::create_field('Time', $input);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$time->getValue(),
|
||||
"Date parsed from {$input} should be {$expected}"
|
||||
);
|
||||
$field = new DBTime('MyField');
|
||||
$field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
|
||||
public function testNice()
|
||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\ORM\Tests;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\ORM\FieldType\DBYear;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
class DBYearTest extends SapphireTest
|
||||
{
|
||||
@ -18,15 +19,15 @@ class DBYearTest extends SapphireTest
|
||||
$field = $year->scaffoldFormField("YearTest");
|
||||
$this->assertEquals(DropdownField::class, get_class($field));
|
||||
|
||||
//This should be a list of years from the current one, counting down to 1900
|
||||
//This should be a list of years from the current one, counting down to 1901
|
||||
$source = $field->getSource();
|
||||
|
||||
$lastValue = end($source);
|
||||
$lastKey = key($source ?? []);
|
||||
|
||||
//Keys and values should be the same - and the last one should be 1900
|
||||
$this->assertEquals(1900, $lastValue);
|
||||
$this->assertEquals(1900, $lastKey);
|
||||
//Keys and values should be the same - and the last one should be 1901
|
||||
$this->assertEquals(1901, $lastValue);
|
||||
$this->assertEquals(1901, $lastKey);
|
||||
}
|
||||
|
||||
public function testScaffoldFormFieldLast()
|
||||
@ -43,4 +44,98 @@ class DBYearTest extends SapphireTest
|
||||
$this->assertEquals($currentYear, $firstValue);
|
||||
$this->assertEquals($currentYear, $firstKey);
|
||||
}
|
||||
|
||||
public static function provideSetValue(): array
|
||||
{
|
||||
return [
|
||||
'4-int' => [
|
||||
'value' => 2024,
|
||||
'expected' => 2024,
|
||||
],
|
||||
'2-int' => [
|
||||
'value' => 24,
|
||||
'expected' => 2024,
|
||||
],
|
||||
'0-int' => [
|
||||
'value' => 0,
|
||||
'expected' => 0,
|
||||
],
|
||||
'4-string' => [
|
||||
'value' => '2024',
|
||||
'expected' => 2024,
|
||||
],
|
||||
'2-string' => [
|
||||
'value' => '24',
|
||||
'expected' => 2024,
|
||||
],
|
||||
'0-string' => [
|
||||
'value' => '0',
|
||||
'expected' => 0,
|
||||
],
|
||||
'00-string' => [
|
||||
'value' => '00',
|
||||
'expected' => 2000,
|
||||
],
|
||||
'0000-string' => [
|
||||
'value' => '0000',
|
||||
'expected' => 0,
|
||||
],
|
||||
'4-int-low' => [
|
||||
'value' => 1900,
|
||||
'expected' => 1900,
|
||||
],
|
||||
'4-int-low' => [
|
||||
'value' => 2156,
|
||||
'expected' => 2156,
|
||||
],
|
||||
'4-string-low' => [
|
||||
'value' => '1900',
|
||||
'expected' => 1900,
|
||||
],
|
||||
'4-string-low' => [
|
||||
'value' => '2156',
|
||||
'expected' => 2156,
|
||||
],
|
||||
'int-negative' => [
|
||||
'value' => -2024,
|
||||
'expected' => -2024,
|
||||
],
|
||||
'string-negative' => [
|
||||
'value' => '-2024',
|
||||
'expected' => '-2024',
|
||||
],
|
||||
'float' => [
|
||||
'value' => 2024.0,
|
||||
'expected' => 2024.0,
|
||||
],
|
||||
'string-float' => [
|
||||
'value' => '2024.0',
|
||||
'expected' => '2024.0',
|
||||
],
|
||||
'null' => [
|
||||
'value' => null,
|
||||
'expected' => null,
|
||||
],
|
||||
'true' => [
|
||||
'value' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'false' => [
|
||||
'value' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'array' => [
|
||||
'value' => [],
|
||||
'expected' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideSetValue')]
|
||||
public function testSetValue(mixed $value, mixed $expected): void
|
||||
{
|
||||
$field = new DBYear('MyField');
|
||||
$result = $field->setValue($value);
|
||||
$this->assertSame($expected, $field->getValue());
|
||||
}
|
||||
}
|
||||
|
@ -24,22 +24,23 @@ class DecimalTest extends SapphireTest
|
||||
{
|
||||
parent::setUp();
|
||||
$this->testDataObject = $this->objFromFixture(DecimalTest\TestObject::class, 'test-dataobject');
|
||||
$x=1;
|
||||
}
|
||||
|
||||
public function testDefaultValue()
|
||||
{
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
0.0,
|
||||
$this->testDataObject->MyDecimal1,
|
||||
0,
|
||||
'Database default for Decimal type is 0'
|
||||
'Database default for Decimal type is 0.0'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSpecifiedDefaultValue()
|
||||
{
|
||||
$this->assertEquals(
|
||||
$this->testDataObject->MyDecimal2,
|
||||
$this->assertSame(
|
||||
2.5,
|
||||
$this->testDataObject->MyDecimal2,
|
||||
'Default value for Decimal type is set to 2.5'
|
||||
);
|
||||
}
|
||||
@ -52,37 +53,37 @@ class DecimalTest extends SapphireTest
|
||||
|
||||
public function testSpecifiedDefaultValueInDefaultsArray()
|
||||
{
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
$this->testDataObject->MyDecimal4,
|
||||
4,
|
||||
4.0,
|
||||
'Default value for Decimal type is set to 4'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLongValueStoredCorrectly()
|
||||
{
|
||||
$this->assertEquals(
|
||||
$this->testDataObject->MyDecimal5,
|
||||
$this->assertSame(
|
||||
1.0,
|
||||
$this->testDataObject->MyDecimal5,
|
||||
'Long default long decimal value is rounded correctly'
|
||||
);
|
||||
|
||||
$this->assertEqualsWithDelta(
|
||||
$this->testDataObject->MyDecimal5,
|
||||
0.99999999999999999999,
|
||||
$this->testDataObject->MyDecimal5,
|
||||
PHP_FLOAT_EPSILON,
|
||||
'Long default long decimal value is correct within float epsilon'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->testDataObject->MyDecimal6,
|
||||
$this->assertSame(
|
||||
8.0,
|
||||
$this->testDataObject->MyDecimal6,
|
||||
'Long decimal value with a default value is rounded correctly'
|
||||
);
|
||||
|
||||
$this->assertEqualsWithDelta(
|
||||
$this->testDataObject->MyDecimal6,
|
||||
7.99999999999999999999,
|
||||
$this->testDataObject->MyDecimal6,
|
||||
PHP_FLOAT_EPSILON,
|
||||
'Long decimal value is within epsilon if longer than allowed number of float digits'
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user