Compare commits

..

2 Commits

Author SHA1 Message Date
Steve Boyd
aca6068d76
Merge c516612144 into 33929e2992 2024-10-10 06:59:13 +00:00
Steve Boyd
c516612144 NEW Validate DBFields 2024-10-10 19:59:07 +13:00
37 changed files with 508 additions and 417 deletions

View File

@ -37,10 +37,18 @@ class NumericFieldValidator extends FieldValidator
$result->addFieldError($this->name, $message, value: $this->value);
return $result;
} elseif (isset($this->minValue) && $this->value < $this->minValue) {
$message = _t(__CLASS__ . '.TOOSMALL', 'Value is too small');
$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 is too large');
$message = _t(
__CLASS__ . '.TOOLARGE',
'Value cannot be greater than {maxValue}',
['maxValue' => $this->maxValue]
);
$result->addFieldError($this->name, $message, value: $this->value);
}
return $result;

View File

@ -1,42 +0,0 @@
<?php
namespace SilverStripe\Core\Validation\FieldValidation;
use SilverStripe\Core\Validation\ValidationResult;
use SilverStripe\Core\Validation\FieldValidation\FieldValidator;
/**
* Validates that a value is a valid year greater than 1901, which is the minimum year in MySQL
*
* Years must be a four digit number greater than 1901, or be between 0 or 99
* 0 is used to represent a null value which will be stored as 0000 in MySQL
* '00' and '0000' are special valid years that may also be used to represent 2000 and null respectively
*
* Both string and integer values are accepted
*
* https://dev.mysql.com/doc/refman/8.0/en/year.html
*/
class YearFieldValidator extends FieldValidator
{
protected function validateValue(): ValidationResult
{
$result = ValidationResult::create();
if ($this->value === '00' || $this->value === '0000') {
return $result;
}
if (!is_int($this->value) && !(is_string($this->value))
|| !preg_match('#^\d+$#', (string) $this->value)
) {
$message = _t(__CLASS__ . '.INVALID', 'Must be an integer or integer string');
$result->addFieldError($this->name, $message, value: $this->value);
return $result;
}
$int = (int) $this->value;
if ($int < 0 || ($int > 99 && $int < 1901) || $int > 2155) {
$message = _t(__CLASS__ . '.INVALID', 'Invalid year');
$result->addFieldError($this->name, $message, value: $this->value);
return $result;
}
return $result;
}
}

View File

@ -2,7 +2,7 @@
namespace SilverStripe\Forms;
use SilverStripe\Core\Validation\FieldValidation\EmailValidator;
use SilverStripe\Core\Validation\FieldValidation\EmailFieldValidator;
/**
* Text input field with validation for correct email format according to the relevant RFC.
@ -10,7 +10,7 @@ use SilverStripe\Core\Validation\FieldValidation\EmailValidator;
class EmailField extends TextField
{
private static array $field_validators = [
EmailValidator::class,
EmailFieldValidator::class,
];
protected $inputType = 'email';

View File

@ -49,6 +49,14 @@ class TextField extends FormField implements TippableFieldInterface
parent::__construct($name, $title, $value);
}
public function setValue($value, $data = null)
{
parent::setValue($value, $data = null);
if (is_null($this->value)) {
$this->value = '';
}
}
/**
* @param int $maxLength
* @return $this

View File

@ -90,7 +90,7 @@ class DBBoolean extends DBField
->setEmptyString($anyText);
}
public function nullValue(): ?int
public function nullValue(): int
{
return 0;
}

View File

@ -106,7 +106,7 @@ class DBDecimal extends DBField
->setScale($this->decimalSize);
}
public function nullValue(): ?int
public function nullValue(): int
{
return 0;
}

View File

@ -10,6 +10,7 @@ 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.
@ -19,7 +20,7 @@ use SilverStripe\ORM\DB;
class DBEnum extends DBString
{
private static array $field_validators = [
EnumFieldValidator::class => ['getEnum'],
EnumFieldValidator::class => ['getEnumObsolete'],
];
/**
@ -78,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 {
@ -247,4 +248,13 @@ class DBEnum extends DBString
$this->setDefaultValue($default);
return $this;
}
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
{
parent::setValue($value, $record, $markChanged);
if (empty($this->value)) {
$this->value = $this->getDefault();
}
return $this;
}
}

View File

@ -119,7 +119,6 @@ abstract class DBField extends ModelData implements DBIndexable, FieldValidation
public function __construct(?string $name = null, array $options = [])
{
$this->name = $name;
$this->value = $this->getDefaultValue();
if ($options) {
if (!is_array($options)) {
@ -127,6 +126,8 @@ abstract class DBField extends ModelData implements DBIndexable, FieldValidation
}
$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();
}

View File

@ -57,7 +57,7 @@ class DBFloat extends DBField
return $field;
}
public function nullValue(): ?int
public function nullValue(): int
{
return 0;
}

View File

@ -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);
}
}

View File

@ -36,17 +36,11 @@ class DBInt extends DBField
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
{
if (is_null($value)) {
// Convert null to 0 so that it will pass validation
// Will be converted to 0 in prepValueForDB(), which is called after validation
// Methods such as DataObject::dbObject() can set this to null e.g. when a value has
// not been explicity set on a new record.
$value = 0;
} elseif (is_string($value) && preg_match('/^-?\d+$/', $value)) {
// Cast int like strings as ints
$value = (int) $value;
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;
}
$this->value = $value;
return $this;
}
@ -91,7 +85,7 @@ class DBInt extends DBField
return NumericField::create($this->name, $title);
}
public function nullValue(): ?int
public function nullValue(): int
{
return 0;
}

View File

@ -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);
}
@ -82,13 +90,14 @@ abstract class DBString extends DBField
return $value || (is_string($value) && strlen($value ?? ''));
}
public function getValueForValidation(): mixed
public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
{
$value = parent::getValueForValidation();
if (is_null($value)) {
return '';
$this->value = '';
} else {
$this->value = $value;
}
return $value;
return $this;
}
public function prepValueForDB(mixed $value): array|string|null

View File

@ -2,18 +2,24 @@
namespace SilverStripe\ORM\FieldType;
use SilverStripe\Core\Validation\FieldValidation\YearFieldValidator;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FormField;
use SilverStripe\ORM\DB;
use SilverStripe\Model\ModelData;
use SilverStripe\Core\Validation\FieldValidation\IntFieldValidator;
/**
* 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
private const MIN_YEAR = 1901;
private const MAX_YEAR = 2155;
private static $field_validators = [
YearFieldValidator::class,
IntFieldValidator::class => ['getMinYear', 'getMaxYear'],
];
public function requireField(): void
@ -30,16 +36,56 @@ class DBYear extends DBField
return $selectBox;
}
public function nullValue(): ?int
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 nullValue(): int
{
return 0;
}
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
@ -47,12 +93,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) {
// 1901 is used as it's the lowest year supported by MySQL
// https://dev.mysql.com/doc/refman/8.0/en/year.html
$end = 1901;
$end = DBYear::MIN_YEAR;
}
$years = [];
for ($i = $start; $i >= $end; $i--) {

View File

@ -68,7 +68,7 @@ class BigIntFieldValidatorTest extends SapphireTest
if ($value === '-9223372036854775808') {
$value = (int) $value;
}
$validator = new BigIntFieldValidator('MyField', $value, false);
$validator = new BigIntFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -65,7 +65,7 @@ class BooleanIntFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new BooleanIntFieldValidator('MyField', $value, false);
$validator = new BooleanIntFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -41,7 +41,7 @@ class DateFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new DateFieldValidator('MyField', $value, false);
$validator = new DateFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -45,7 +45,7 @@ class DatetimeFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new DatetimeFieldValidator('MyField', $value, false);
$validator = new DatetimeFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -131,7 +131,7 @@ class DecimalFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, int $wholeSize, int $decimalSize, bool $expected): void
{
$validator = new DecimalFieldValidator('MyField', $value, false, $wholeSize, $decimalSize);
$validator = new DecimalFieldValidator('MyField', $value, $wholeSize, $decimalSize);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -30,7 +30,7 @@ class EmailFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new EmailFieldValidator('MyField', $value, false);
$validator = new EmailFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -47,7 +47,7 @@ class EnumFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, array $allowedValues, bool $expected): void
{
$validator = new EnumFieldValidator('MyField', $value, false, $allowedValues);
$validator = new EnumFieldValidator('MyField', $value, $allowedValues);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -69,7 +69,7 @@ class IntFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new IntFieldValidator('MyField', $value, false);
$validator = new IntFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -38,7 +38,7 @@ class IpFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new IpFieldValidator('MyField', $value, false);
$validator = new IpFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -54,7 +54,7 @@ class LocaleFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new LocaleFieldValidator('MyField', $value, false);
$validator = new LocaleFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -75,7 +75,7 @@ class MultiEnumFieldValidatorTest extends SapphireTest
if ($exception) {
$this->expectException(InvalidArgumentException::class);
}
$validator = new MultiEnumFieldValidator('MyField', $value, false, $allowedValues);
$validator = new MultiEnumFieldValidator('MyField', $value, $allowedValues);
$result = $validator->validate();
if (!$exception) {
$this->assertSame($expected, $result->isValid());

View File

@ -73,7 +73,7 @@ class NumericFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new NumericFieldValidator('MyField', $value, false);
$validator = new NumericFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -140,7 +140,7 @@ class StringFieldValidatorTest extends SapphireTest
if ($exception) {
$this->expectException(InvalidArgumentException::class);
}
$validator = new StringFieldValidator('MyField', $value, false, $minLength, $maxLength);
$validator = new StringFieldValidator('MyField', $value, $minLength, $maxLength);
$result = $validator->validate();
if (!$exception) {
$this->assertSame($expected, $result->isValid());

View File

@ -41,7 +41,7 @@ class TimeFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new TimeFieldValidator('MyField', $value, false);
$validator = new TimeFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -38,7 +38,7 @@ class UrlFieldValidatorTest extends SapphireTest
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new UrlFieldValidator('MyField', $value, false);
$validator = new UrlFieldValidator('MyField', $value);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}

View File

@ -1,105 +0,0 @@
<?php
namespace SilverStripe\Core\Tests\Validation\FieldValidation;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\DataProvider;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Core\Validation\FieldValidation\YearFieldValidator;
class YearFieldValidatorTest extends SapphireTest
{
public static function provideValidate(): array
{
return [
'valid-4-int' => [
'value' => 2024,
'expected' => true,
],
'valid-2-int' => [
'value' => 24,
'expected' => true,
],
'valid-0-int' => [
'value' => 0,
'expected' => true,
],
'valid-4-string' => [
'value' => '2024',
'expected' => true,
],
'valid-2-string' => [
'value' => '24',
'expected' => true,
],
'valid-0-string' => [
'value' => '0',
'expected' => true,
],
'valid-00-string' => [
'value' => '00',
'expected' => true,
],
'valid-0000-string' => [
'value' => '0000',
'expected' => true,
],
'invalid-4-int-low' => [
'value' => 1900,
'expected' => false,
],
'invalid-4-int-low' => [
'value' => 2156,
'expected' => false,
],
'invalid-4-string-low' => [
'value' => '1900',
'expected' => false,
],
'invalid-4-string-low' => [
'value' => '2156',
'expected' => false,
],
'invalid-int-negative' => [
'value' => -2024,
'expected' => false,
],
'invalid-string-negative' => [
'value' => '-2024',
'expected' => false,
],
'invalid-float' => [
'value' => 2024.0,
'expected' => false,
],
'invalid-string-float' => [
'value' => '2024.0',
'expected' => false,
],
'invalid-null' => [
'value' => null,
'expected' => false,
],
'invalid-true' => [
'value' => true,
'expected' => false,
],
'invalid-false' => [
'value' => false,
'expected' => false,
],
'invalid-array' => [
'value' => [],
'expected' => false,
],
];
}
#[DataProvider('provideValidate')]
public function testValidate(mixed $value, bool $expected): void
{
$validator = new YearFieldValidator('MyField', $value, false);
$result = $validator->validate();
$this->assertSame($expected, $result->isValid());
}
}

View File

@ -6,6 +6,7 @@ use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\Tip;
use PHPUnit\Framework\Attributes\DataProvider;
class TextFieldTest extends SapphireTest
{
@ -45,4 +46,42 @@ class TextFieldTest extends SapphireTest
$textField->setTip(new Tip('TestTip'));
$this->assertArrayHasKey('tip', $textField->getSchemaDataDefaults());
}
public static function provideSetValue(): array
{
return [
'string' => [
'value' => 'fish',
'expected' => 'fish',
],
'string-blank' => [
'value' => '',
'expected' => '',
],
'null' => [
'value' => null,
'expected' => '',
],
'zero' => [
'value' => 0,
'expected' => 0,
],
'true' => [
'value' => true,
'expected' => true,
],
'false' => [
'value' => false,
'expected' => false,
],
];
}
#[DataProvider('provideSetValue')]
public function testSetValue(mixed $value, mixed $expected): void
{
$field = new TextField('TestField');
$field->setValue($value);
$this->assertSame($expected, $field->getValue());
}
}

View File

@ -141,4 +141,38 @@ 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' => 'blue',
],
'null' => [
'value' => null,
'expected' => 'blue',
],
];
}
#[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());
}
}

View File

@ -2,6 +2,7 @@
namespace SilverStripe\ORM\Tests;
use Exception;
use SilverStripe\Assets\Image;
use SilverStripe\ORM\FieldType\DBBigInt;
use SilverStripe\ORM\FieldType\DBBoolean;
@ -32,8 +33,31 @@ use SilverStripe\ORM\FieldType\DBYear;
use PHPUnit\Framework\Attributes\DataProvider;
use SilverStripe\Core\ClassInfo;
use ReflectionClass;
use SilverStripe\Core\Validation\FieldValidation\BooleanIntFieldValidator;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\FieldType\DBComposite;
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\Assets\Storage\DBFile;
use SilverStripe\ORM\FieldType\DBClassNameVarchar;
/**
* Tests for DBField objects.
@ -408,7 +432,7 @@ class DBFieldTest extends SapphireTest
];
$classes = ClassInfo::subclassesFor(DBField::class);
foreach ($classes as $class) {
if ($class instanceof TestOnly) {
if (is_a($class, TestOnly::class, true)) {
continue;
}
$reflector = new ReflectionClass($class);
@ -426,4 +450,124 @@ class DBFieldTest extends SapphireTest
$this->assertSame($expected, $field->getValue(), $class);
}
}
public function testFieldValidatorConfig(): void
{
$expectedFieldValidators = [
DBBigInt::class => [
BigIntFieldValidator::class,
],
DBBoolean::class => [
BooleanIntFieldValidator::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 => [
StringFieldValidator::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);
}
}

View File

@ -1,205 +0,0 @@
<?php
namespace SilverStripe\ORM\Tests;
use ReflectionMethod;
use SilverStripe\Dev\SapphireTest;
use PHPUnit\Framework\Attributes\DataProvider;
use SilverStripe\ORM\FieldType\DBBigInt;
use SilverStripe\ORM\FieldType\DBBoolean;
use SilverStripe\ORM\FieldType\DBClassName;
use SilverStripe\ORM\FieldType\DBComposite;
use SilverStripe\ORM\FieldType\DBCurrency;
use SilverStripe\ORM\FieldType\DBInt;
use SilverStripe\Core\Validation\FieldValidation\IntValidator;
use SilverStripe\Core\Validation\FieldValidation\BigIntValidator;
use SilverStripe\ORM\FieldType\DBDate;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBDecimal;
use SilverStripe\ORM\FieldType\DBEmail;
use SilverStripe\ORM\FieldType\DBFloat;
use SilverStripe\ORM\FieldType\DBForeignKey;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\FieldType\DBHTMLVarchar;
use SilverStripe\ORM\FieldType\DBIndexable;
use SilverStripe\ORM\FieldType\DBIp;
use SilverStripe\ORM\FieldType\DBLocale;
use SilverStripe\ORM\FieldType\DBMoney;
use SilverStripe\ORM\FieldType\DBMultiEnum;
use SilverStripe\ORM\FieldType\DBPercentage;
use SilverStripe\ORM\FieldType\DBPolymorphicForeignKey;
use SilverStripe\ORM\FieldType\DBPolymorphicRelationAwareForeignKey;
use SilverStripe\ORM\FieldType\DBPrimaryKey;
use SilverStripe\ORM\FieldType\DBString;
use SilverStripe\ORM\FieldType\DBText;
use SilverStripe\ORM\FieldType\DBTime;
use SilverStripe\ORM\FieldType\DBUrl;
use SilverStripe\ORM\FieldType\DBVarchar;
use SilverStripe\ORM\FieldType\DBYear;
class DBFieldValidatorsTest extends SapphireTest
{
public static function provideFieldValidatorConfig(): array
{
return [
'DBBigInt' => [
'class' => DBBigInt::class,
'expected' => [
BigIntValidator::class,
],
],
'DBBoolean' => [
'class' => DBBoolean::class,
'expected' => [
],
],
'DBClassName' => [
'class' => DBClassName::class,
'expected' => [
],
],
'DBComposite' => [
'class' => DBComposite::class,
'expected' => [
],
],
'DBCurrency' => [
'class' => DBCurrency::class,
'expected' => [
],
],
'DBDate' => [
'class' => DBDate::class,
'expected' => [
],
],
'DBDatetime' => [
'class' => DBDatetime::class,
'expected' => [
],
],
'DBDecimal' => [
'class' => DBDecimal::class,
'expected' => [
],
],
'DBEmail' => [
'class' => DBEmail::class,
'expected' => [
],
],
'DBFloat' => [
'class' => DBFloat::class,
'expected' => [
],
],
'DBForeignKey' => [
'class' => DBForeignKey::class,
'expected' => [
],
],
'DBHTMLText' => [
'class' => DBHTMLText::class,
'expected' => [
],
],
'DBHTMLVarchar' => [
'class' => DBHTMLVarchar::class,
'expected' => [
],
],
'DBIndexable' => [
'class' => DBIndexable::class,
'expected' => [
],
],
'DBInt' => [
'class' => DBInt::class,
'expected' => [
IntValidator::class,
],
],
'DBIp' => [
'class' => DBIp::class,
'expected' => [
],
],
'DBLocale' => [
'class' => DBLocale::class,
'expected' => [
],
],
'DBMoney' => [
'class' => DBMoney::class,
'expected' => [
],
],
'DBMultiEnum' => [
'class' => DBMultiEnum::class,
'expected' => [
],
],
'DBPercentage' => [
'class' => DBPercentage::class,
'expected' => [
],
],
'DBPolymorphicForeignKey' => [
'class' => DBPolymorphicForeignKey::class,
'expected' => [
],
],
'DBPolymorhicRelationAwareForiegnKey' => [
'class' => DBPolymorphicRelationAwareForeignKey::class,
'expected' => [
],
],
'DBPrimaryKey' => [
'class' => DBPrimaryKey::class,
'expected' => [
],
],
'DBString' => [
'class' => DBString::class,
'expected' => [
],
],
'DBText' => [
'class' => DBText::class,
'expected' => [
],
],
'DBTime' => [
'class' => DBTime::class,
'expected' => [
],
],
'DBUrl' => [
'class' => DBUrl::class,
'expected' => [
],
],
'DBVarchar' => [
'class' => DBVarchar::class,
'expected' => [
],
],
'DBYear' => [
'class' => DBYear::class,
'expected' => [
],
],
];
}
#[DataProvider('provideFieldValidatorConfig')]
public function testFieldValidatorConfig(string $class, array $expected): void
{
$method = new ReflectionMethod($class, 'getFieldValidators');
$method->setAccessible(true);
$obj = new $class('MyField');
$fieldValidators = $method->invoke($obj);
$actual = array_map('get_class', $fieldValidators);
$this->assertSame($expected, $actual);
}
}

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

View File

@ -14,7 +14,7 @@ class DBIntTest extends SapphireTest
$this->assertSame(0, $field->getValue());
}
public static function provideSetGetValue(): array
public static function provideSetValue(): array
{
return [
'int' => [
@ -25,6 +25,14 @@ class DBIntTest extends SapphireTest
'value' => '3',
'expected' => 3,
],
'negative-int' => [
'value' => -3,
'expected' => -3,
],
'negative-string-int' => [
'value' => '-3',
'expected' => -3,
],
'string' => [
'value' => 'fish',
'expected' => 'fish',
@ -35,15 +43,15 @@ class DBIntTest extends SapphireTest
],
'null' => [
'value' => null,
'expected' => 0,
'expected' => null,
],
];
}
#[DataProvider('provideSetGetValue')]
public function testSetGetValue(mixed $value, mixed $expected): void
#[DataProvider('provideSetValue')]
public function testSetValue(mixed $value, mixed $expected): void
{
$field = DBInt::create('MyField');
$field = new DBInt('MyField');
$field->setValue($value);
$this->assertSame($expected, $field->getValue());
}

View File

@ -70,7 +70,7 @@ class DBStringTest extends SapphireTest
$this->assertFalse(DBField::create_field(MyStringField::class, 0.0)->exists());
}
public static function provideValueForValidation(): array
public static function provideSetValue(): array
{
return [
'string' => [
@ -88,11 +88,11 @@ class DBStringTest extends SapphireTest
];
}
#[DataProvider('provideValueForValidation')]
public function getValueForValidation(mixed $value, string $expected): void
#[DataProvider('provideSetValue')]
public function testSetValue(mixed $value, string $expected): void
{
$obj = new MyStringField('TestField');
$obj->setValue($value);
$this->assertSame($expected, $obj->getValueForValidation());
$this->assertSame($expected, $obj->getValue());
}
}

View File

@ -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());
}
}