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
17 changed files with 175 additions and 113 deletions

View File

@ -16,8 +16,8 @@ class DateFieldValidator extends FieldValidator
protected function validateValue(): ValidationResult
{
$result = ValidationResult::create();
// Allow empty values
if (!$this->value) {
// Allow empty strings
if ($this->value === '') {
return $result;
}
// Not using symfony/validator because it was allowing d-m-Y format strings

View File

@ -117,6 +117,31 @@ class TextField extends FormField implements TippableFieldInterface
return $data;
}
/**
* Validate this field
*
* @param Validator $validator
* @return bool
*/
public function validate($validator)
{
$result = true;
if (!is_null($this->maxLength) && mb_strlen($this->value ?? '') > $this->maxLength) {
$name = strip_tags($this->Title() ? $this->Title() : $this->getName());
$validator->validationError(
$this->name,
_t(
'SilverStripe\\Forms\\TextField.VALIDATEMAXLENGTH',
'The value for {name} must not exceed {maxLength} characters in length',
['name' => $name, 'maxLength' => $this->maxLength]
),
"validation"
);
$result = false;
}
return $this->extendValidationResult($result, $validator);
}
public function getSchemaValidation()
{
$rules = parent::getSchemaValidation();

View File

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

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

@ -27,11 +27,6 @@ class DBDecimal extends DBField
*/
protected int $decimalSize = 2;
/**
* Default value
*/
protected float|int|string $defaultValue = 0;
/**
* Create a new Decimal field.
*/
@ -70,7 +65,7 @@ class DBDecimal extends DBField
$parts = [
'datatype' => 'decimal',
'precision' => "$this->wholeSize,$this->decimalSize",
'default' => $this->defaultValue,
'default' => $this->getDefaultValue(),
'arrayValue' => $this->arrayValue
];

View File

@ -5,6 +5,7 @@ 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;
@ -16,7 +17,8 @@ use SilverStripe\ORM\DB;
class DBMultiEnum extends DBEnum
{
private static array $field_validators = [
// disable parent field validator
// disable parent field validators
StringFieldValidator::class => null,
EnumFieldValidator::class => null,
// enable multi enum field validator
MultiEnumFieldValidator::class => ['getEnum'],

View File

@ -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

View File

@ -11,20 +11,20 @@ class BooleanFieldValidatorTest extends SapphireTest
public static function provideValidate(): array
{
return [
'valid-int-1' => [
'value' => 1,
'expected' => true,
],
'valid-int-0' => [
'value' => 0,
'expected' => true,
],
'invvalid-true' => [
'valid-true' => [
'value' => true,
'expected' => true,
],
'valid-false' => [
'value' => false,
'expected' => true,
],
'invalid-int-1' => [
'value' => 1,
'expected' => false,
],
'invalid-false' => [
'value' => false,
'invalid-int-0' => [
'value' => 0,
'expected' => false,
],
'invalid-string-1' => [

View File

@ -15,6 +15,10 @@ class DateFieldValidatorTest extends SapphireTest
'value' => '2020-09-15',
'expected' => true,
],
'valid-blank-string' => [
'value' => '',
'expected' => true,
],
'invalid' => [
'value' => '2020-02-30',
'expected' => false,

View File

@ -21,16 +21,16 @@ class EnumFieldValidatorTest extends SapphireTest
'allowedValues' => [123, 456],
'expected' => true,
],
'valid-none' => [
'value' => '',
'allowedValues' => ['cat', 'dog'],
'expected' => true,
],
'invalid' => [
'value' => 'fish',
'allowedValues' => ['cat', 'dog'],
'expected' => false,
],
'invalid-none' => [
'value' => '',
'allowedValues' => ['cat', 'dog'],
'expected' => false,
],
'invalid-null' => [
'value' => null,
'allowedValues' => ['cat', 'dog'],

View File

@ -6,7 +6,6 @@ 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
{
@ -46,42 +45,4 @@ 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

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

View File

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

View File

@ -159,11 +159,11 @@ class DBEnumTest extends SapphireTest
],
'empty-string' => [
'value' => '',
'expected' => 'blue',
'expected' => '',
],
'null' => [
'value' => null,
'expected' => 'blue',
'expected' => null,
],
];
}
@ -175,4 +175,14 @@ class DBEnumTest extends SapphireTest
$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);
}
}

View File

@ -425,16 +425,20 @@ class DBFieldTest extends SapphireTest
{
$expectedBaseDefault = null;
$expectedDefaults = [
DBBoolean::class => 0,
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;
@ -448,7 +452,11 @@ class DBFieldTest extends SapphireTest
}
$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

View File

@ -17,7 +17,7 @@ class DBForiegnKeyTest extends SapphireTest
],
'string' => [
'value' => '2',
'expected' => '2',
'expected' => 2,
],
'zero' => [
'value' => 0,

View File

@ -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->testDataObject->MyDecimal1,
$this->assertSame(
0,
$this->testDataObject->MyDecimal1,
'Database default for Decimal type is 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'
);
}
@ -61,28 +62,28 @@ class DecimalTest extends SapphireTest
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'
);