Compare commits

..

2 Commits

Author SHA1 Message Date
Steve Boyd
226e278719
Merge 0d824ccaab into 6bb9a0b33d 2024-10-14 06:53:43 +00:00
Steve Boyd
0d824ccaab NEW Validate DBFields 2024-10-14 19:53:35 +13:00
18 changed files with 71 additions and 181 deletions

View File

@ -23,7 +23,7 @@ class DateFieldValidator extends FieldValidator
// 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);
$result->addFieldError($this->name, $this->getMessage());
}
return $result;
}

View File

@ -8,7 +8,5 @@ interface FieldValidationInterface extends ValidationInterface
{
public function getName(): string;
public function getValue(): mixed;
public function getValueForValidation(): mixed;
}

View File

@ -35,10 +35,6 @@ trait FieldValidatorsTrait
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();

View File

@ -43,7 +43,6 @@ class StringFieldValidator extends FieldValidator
$result->addFieldError($this->name, $message, value: $this->value);
return $result;
}
// TODO this seems non-sensical - should enforce minLength??
// Blank strings are valid, even if there's a minLength requirement
if ($this->value === '') {
return $result;

View File

@ -1257,10 +1257,6 @@ class FormField extends RequestHandler
{
$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();

View File

@ -57,6 +57,11 @@ class DBDate extends DBField
return $this;
}
public function nullValue(): string
{
return '';
}
/**
* Parse timestamp or iso8601-ish date into standard iso8601 format
*

View File

@ -60,7 +60,7 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider
/**
* Flag idicating if this field is considered immutable
* when this is enabled setting the value of this field will return a new field instance
* instead updatin the old one
* instead updating the old one
*/
protected bool $immutable = false;

View File

@ -176,6 +176,11 @@ class DBEnum extends DBString
return $this->enum;
}
public function nullValue(): string
{
return '';
}
/**
* Get the list of enum values, including obsolete values still present in the database
*

View File

@ -90,6 +90,11 @@ abstract class DBString extends DBField
return $value || (is_string($value) && strlen($value ?? ''));
}
public function nullValue(): string
{
return '';
}
public function prepValueForDB(mixed $value): array|string|null
{
// Cast non-empty value

View File

@ -44,13 +44,16 @@ class DBTime extends DBField
/**
* 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): mixed
protected function parseTime(mixed $value): string|null|false
{
// Return empty dates as-is, they will get caught by validator later
// Skip empty values
if (empty($value) && !is_numeric($value)) {
return $value;
return null;
}
// Determine value to parse
if (is_array($value)) {
$source = $value; // parse array
@ -60,16 +63,14 @@ 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());
$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;
return $formatter->format($source);
}
/**

View File

@ -66,6 +66,11 @@ class DBYear extends DBField
return $this;
}
public function nullValue(): int
{
return 0;
}
public function getMinYear(): int
{
return DBYear::MIN_YEAR;

View File

@ -444,8 +444,8 @@ class Member extends DataObject
if (!$this->PasswordExpiry) {
return false;
}
$expired = strtotime(date('Y-m-d')) >= strtotime($this->PasswordExpiry ?? '');
return $expired;
return strtotime(date('Y-m-d')) >= strtotime($this->PasswordExpiry ?? '');
}
/**

View File

@ -39,7 +39,6 @@ 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
{

View File

@ -181,21 +181,9 @@ class DBDateTest extends SapphireTest
);
}
public static function provideSetValue()
public static function provideSetNullAndZeroValues()
{
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' => ''
@ -220,9 +208,9 @@ class DBDateTest extends SapphireTest
'value' => 0,
'expected' => '1970-01-01'
],
'zero-date' => [
'value' => '0000-00-00',
'expected' => '0000-00-00'
'zero-datetime' => [
'value' => '0000-00-00 00:00:00',
'expected' => '0000-00-00 00:00:00'
],
'zero-date-slashes' => [
'value' => '00/00/0000',
@ -239,12 +227,11 @@ class DBDateTest extends SapphireTest
];
}
#[DataProvider('provideSetValue')]
public function testSetValue(mixed $value, mixed $expected)
#[DataProvider('provideSetNullAndZeroValues')]
public function testSetNullAndZeroValues(mixed $value, mixed $expected)
{
$field = new DBDate('MyField');
$field->setValue($value);
$this->assertSame($expected, $field->getValue());
$date = DBField::create_field('Date', $value);
$this->assertSame($expected, $date->getValue());
}
public function testDayOfMonth()

View File

@ -330,50 +330,4 @@ 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());
}
}

View File

@ -3,6 +3,7 @@
namespace SilverStripe\ORM\Tests;
use Exception;
use SilverStripe\Assets\Image;
use SilverStripe\ORM\FieldType\DBBigInt;
use SilverStripe\ORM\FieldType\DBBoolean;
use SilverStripe\ORM\FieldType\DBCurrency;
@ -55,6 +56,7 @@ 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;
/**
@ -520,6 +522,7 @@ class DBFieldTest extends SapphireTest
CompositeFieldValidator::class,
],
DBMultiEnum::class => [
StringFieldValidator::class,
MultiEnumFieldValidator::class,
],
DBPercentage::class => [
@ -575,22 +578,4 @@ class DBFieldTest extends SapphireTest
// 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());
}
}

View File

@ -17,78 +17,33 @@ class DBTimeTest extends SapphireTest
i18n::set_locale('en_NZ');
}
public static function provideSetValue()
public static function dataTestParse()
{
return [
'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' => []
],
// 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'],
];
}
#[DataProvider('provideSetValue')]
public function testSetValue(mixed $value, mixed $expected)
/**
* @param string $input
* @param string $expected
*/
#[DataProvider('dataTestParse')]
public function testParse($input, $expected)
{
$field = new DBTime('MyField');
$field->setValue($value);
$this->assertSame($expected, $field->getValue());
$time = DBField::create_field('Time', $input);
$this->assertEquals(
$expected,
$time->getValue(),
"Date parsed from {$input} should be {$expected}"
);
}
public function testNice()

View File

@ -30,9 +30,9 @@ class DecimalTest extends SapphireTest
public function testDefaultValue()
{
$this->assertSame(
0.0,
0,
$this->testDataObject->MyDecimal1,
'Database default for Decimal type is 0.0'
'Database default for Decimal type is 0'
);
}
@ -53,9 +53,9 @@ class DecimalTest extends SapphireTest
public function testSpecifiedDefaultValueInDefaultsArray()
{
$this->assertSame(
$this->assertEquals(
$this->testDataObject->MyDecimal4,
4.0,
4,
'Default value for Decimal type is set to 4'
);
}