From 4a860dca0c6b7e21870bef24617e614f5b4bf112 Mon Sep 17 00:00:00 2001 From: Carlos Jr Date: Thu, 30 Jan 2025 20:39:02 -0300 Subject: [PATCH 1/2] Add fluent numeric rule --- src/Illuminate/Validation/Rule.php | 11 + src/Illuminate/Validation/Rules/Numeric.php | 230 ++++++++++++ .../Validation/ValidationNumericRuleTest.php | 349 ++++++++++++++++++ 3 files changed, 590 insertions(+) create mode 100644 src/Illuminate/Validation/Rules/Numeric.php create mode 100644 tests/Validation/ValidationNumericRuleTest.php diff --git a/src/Illuminate/Validation/Rule.php b/src/Illuminate/Validation/Rule.php index c9d9a2bad92e..c15772bba7d5 100644 --- a/src/Illuminate/Validation/Rule.php +++ b/src/Illuminate/Validation/Rule.php @@ -16,6 +16,7 @@ use Illuminate\Validation\Rules\ImageFile; use Illuminate\Validation\Rules\In; use Illuminate\Validation\Rules\NotIn; +use Illuminate\Validation\Rules\Numeric; use Illuminate\Validation\Rules\ProhibitedIf; use Illuminate\Validation\Rules\RequiredIf; use Illuminate\Validation\Rules\Unique; @@ -232,4 +233,14 @@ public static function dimensions(array $constraints = []) { return new Dimensions($constraints); } + + /** + * Get a numeric rule builder instance. + * + * @return \Illuminate\Validation\Rules\Numeric + */ + public static function numeric() + { + return new Numeric; + } } diff --git a/src/Illuminate/Validation/Rules/Numeric.php b/src/Illuminate/Validation/Rules/Numeric.php new file mode 100644 index 000000000000..405262af1a9b --- /dev/null +++ b/src/Illuminate/Validation/Rules/Numeric.php @@ -0,0 +1,230 @@ +addRule('between:'.$min.','.$max); + } + + /** + * The field under validation must contain the specified number of decimal places. + * + * @param int $min + * @param int|null $max + * @return $this + */ + public function decimal(int $min, ?int $max = null): Numeric + { + $rule = 'decimal:'.$min; + + if ($max !== null) { + $rule .= ','.$max; + } + + return $this->addRule($rule); + } + + /** + * The field under validation must have a different value than field. + * + * @param string $field + * @return $this + */ + public function different(string $field): Numeric + { + return $this->addRule('different:'.$field); + } + + /** + * The integer under validation must have an exact length of value. + * + * @param int $length + * @return $this + */ + public function digits(int $length): Numeric + { + return $this->integer()->addRule('digits:'.$length); + } + + /** + * The integer under validation must have a length between the given min and max. + * + * @param int $min + * @param int $max + * @return $this + */ + public function digitsBetween(int $min, int $max): Numeric + { + return $this->integer()->addRule('digits_between:'.$min.','.$max); + } + + /** + * The field under validation must be greater than the given field or value. + * + * @param string $field + * @return $this + */ + public function greaterThan(string $field): Numeric + { + return $this->addRule('gt:'.$field); + } + + /** + * The field under validation must be greater than or equal to the given field or value. + * + * @param string $field + * @return $this + */ + public function greaterThanOrEqual(string $field): Numeric + { + return $this->addRule('gte:'.$field); + } + + /** + * The field under validation must be an integer. + * + * @return $this + */ + public function integer(): Numeric + { + return $this->addRule('integer'); + } + + /** + * The field under validation must be less than the given field. + * + * @param string $field + * @return $this + */ + public function lessThan(string $field): Numeric + { + return $this->addRule('lt:'.$field); + } + + /** + * The field under validation must be less than or equal to the given field. + * + * @param string $field + * @return $this + */ + public function lessThanOrEqual(string $field): Numeric + { + return $this->addRule('lte:'.$field); + } + + /** + * The field under validation must be less than or equal to a maximum value. + * + * @param float|int $value + * @return $this + */ + public function max(float|int $value): Numeric + { + return $this->addRule('max:'.$value); + } + + /** + * The integer under validation must have a maximum length of value. + * + * @param int $value + * @return $this + */ + public function maxDigits(int $value): Numeric + { + return $this->addRule('max_digits:'.$value); + } + + /** + * The field under validation must have a minimum value. + * + * @param float|int $value + * @return $this + */ + public function min(float|int $value): Numeric + { + return $this->addRule('min:'.$value); + } + + /** + * The integer under validation must have a minimum length of value. + * + * @param int $value + * @return $this + */ + public function minDigits(int $value): Numeric + { + return $this->addRule('min_digits:'.$value); + } + + /** + * The field under validation must be a multiple of value. + * + * @param float|int $value + * @return $this + */ + public function multipleOf(float|int $value): Numeric + { + return $this->addRule('multiple_of:'.$value); + } + + /** + * The given field must match the field under validation. + * + * @param string $field + * @return $this + */ + public function same(string $field): Numeric + { + return $this->addRule('same:'.$field); + } + + /** + * The field under validation must have a size matching the given value. + * + * @param int $value + * @return $this + */ + public function size(int $value): Numeric + { + return $this->integer()->addRule('size:'.$value); + } + + /** + * Convert the rule to a validation string. + */ + public function __toString(): string + { + return implode('|', array_unique($this->constraints)); + } + + /** + * Add custom rules to the validation rules array. + */ + protected function addRule(array|string $rules): Numeric + { + $this->constraints = array_merge($this->constraints, Arr::wrap($rules)); + + return $this; + } +} diff --git a/tests/Validation/ValidationNumericRuleTest.php b/tests/Validation/ValidationNumericRuleTest.php new file mode 100644 index 000000000000..e6647dacc71d --- /dev/null +++ b/tests/Validation/ValidationNumericRuleTest.php @@ -0,0 +1,349 @@ +assertEquals('numeric', (string) $rule); + + $rule = new Numeric(); + $this->assertSame('numeric', (string) $rule); + } + + public function testBetweenRule() + { + $rule = Rule::numeric()->between(1, 10); + $this->assertEquals('numeric|between:1,10', (string) $rule); + + $rule = Rule::numeric()->between(1.5, 10.5); + $this->assertEquals('numeric|between:1.5,10.5', (string) $rule); + } + + public function testDecimalRule() + { + $rule = Rule::numeric()->decimal(2, 4); + $this->assertEquals('numeric|decimal:2,4', (string) $rule); + + $rule = Rule::numeric()->decimal(2); + $this->assertEquals('numeric|decimal:2', (string) $rule); + } + + public function testDifferentRule() + { + $rule = Rule::numeric()->different('some_field'); + $this->assertEquals('numeric|different:some_field', (string) $rule); + } + + public function testDigitsRule() + { + $rule = Rule::numeric()->digits(10); + $this->assertEquals('numeric|integer|digits:10', (string) $rule); + } + + public function testDigitsBetweenRule() + { + $rule = Rule::numeric()->digitsBetween(2, 10); + $this->assertEquals('numeric|integer|digits_between:2,10', (string) $rule); + } + + public function testGreaterThanRule() + { + $rule = Rule::numeric()->greaterThan('some_field'); + $this->assertEquals('numeric|gt:some_field', (string) $rule); + } + + public function testGreaterThanOrEqualRule() + { + $rule = Rule::numeric()->greaterThanOrEqual('some_field'); + $this->assertEquals('numeric|gte:some_field', (string) $rule); + } + + public function testIntegerRule() + { + $rule = Rule::numeric()->integer(); + $this->assertEquals('numeric|integer', (string) $rule); + } + + public function testLessThanRule() + { + $rule = Rule::numeric()->lessThan('some_field'); + $this->assertEquals('numeric|lt:some_field', (string) $rule); + } + + public function testLessThanOrEqualRule() + { + $rule = Rule::numeric()->lessThanOrEqual('some_field'); + $this->assertEquals('numeric|lte:some_field', (string) $rule); + } + + public function testMaxRule() + { + $rule = Rule::numeric()->max(10); + $this->assertEquals('numeric|max:10', (string) $rule); + + $rule = Rule::numeric()->max(10.5); + $this->assertEquals('numeric|max:10.5', (string) $rule); + } + + public function testMaxDigitsRule() + { + $rule = Rule::numeric()->maxDigits(10); + $this->assertEquals('numeric|max_digits:10', (string) $rule); + } + + public function testMinRule() + { + $rule = Rule::numeric()->min(10); + $this->assertEquals('numeric|min:10', (string) $rule); + + $rule = Rule::numeric()->min(10.5); + $this->assertEquals('numeric|min:10.5', (string) $rule); + } + + public function testMinDigitsRule() + { + $rule = Rule::numeric()->minDigits(10); + $this->assertEquals('numeric|min_digits:10', (string) $rule); + } + + public function testMultipleOfRule() + { + $rule = Rule::numeric()->multipleOf(10); + $this->assertEquals('numeric|multiple_of:10', (string) $rule); + } + + public function testSameRule() + { + $rule = Rule::numeric()->same('some_field'); + $this->assertEquals('numeric|same:some_field', (string) $rule); + } + + public function testSizeRule() + { + $rule = Rule::numeric()->size(10); + $this->assertEquals('numeric|integer|size:10', (string) $rule); + } + + public function testChainedRules() + { + $rule = Rule::numeric() + ->integer() + ->multipleOf(10) + ->lessThanOrEqual('some_field') + ->max(100); + $this->assertEquals('numeric|integer|multiple_of:10|lte:some_field|max:100', (string) $rule); + + $rule = Rule::numeric() + ->decimal(2) + ->when(true, function ($rule) { + $rule->same('some_field'); + }) + ->unless(true, function ($rule) { + $rule->different('some_field_2'); + }); + $this->assertSame('numeric|decimal:2|same:some_field', (string) $rule); + } + + public function testNumericValidation() + { + $trans = new Translator(new ArrayLoader, 'en'); + + $rule = Rule::numeric(); + + $validator = new Validator( + $trans, + ['numeric' => 'NaN'], + ['numeric' => $rule] + ); + + $this->assertSame( + $trans->get('validation.numeric'), + $validator->errors()->first('numeric') + ); + + $validator = new Validator( + $trans, + ['numeric' => '100'], + ['numeric' => $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->between(10, 100); + + $validator = new Validator( + $trans, + ['numeric' => '50'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->different('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '50', 'some_field' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->digits(2); + + $validator = new Validator( + $trans, + ['numeric' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->digitsBetween(2, 4); + + $validator = new Validator( + $trans, + ['numeric' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->greaterThan('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '100', 'some_field' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->greaterThanOrEqual('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '100', 'some_field' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->integer(); + + $validator = new Validator( + $trans, + ['numeric' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->lessThan('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '100', 'some_field' => '150'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->lessThanOrEqual('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '100', 'some_field' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->max(200); + + $validator = new Validator( + $trans, + ['numeric' => '200'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->maxDigits(3); + + $validator = new Validator( + $trans, + ['numeric' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->min(2); + + $validator = new Validator( + $trans, + ['numeric' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->minDigits(2); + + $validator = new Validator( + $trans, + ['numeric' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->multipleOf(10); + + $validator = new Validator( + $trans, + ['numeric' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->same('some_field'); + + $validator = new Validator( + $trans, + ['numeric' => '100', 'some_field' => '100'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + + $rule = Rule::numeric()->size(10); + + $validator = new Validator( + $trans, + ['numeric' => '10'], + ['numeric' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('numeric')); + } + + public function testUniquenessValidation() + { + $rule = Rule::numeric()->integer()->digits(2)->size(2); + $this->assertEquals('numeric|integer|digits:2|size:2', (string) $rule); + } + +} From 23acf6c3fbb5998ed8c2646dbb6881d444985bda Mon Sep 17 00:00:00 2001 From: Carlos Jr Date: Thu, 30 Jan 2025 21:07:26 -0300 Subject: [PATCH 2/2] Fix style --- tests/Validation/ValidationNumericRuleTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Validation/ValidationNumericRuleTest.php b/tests/Validation/ValidationNumericRuleTest.php index e6647dacc71d..c51bced6177f 100644 --- a/tests/Validation/ValidationNumericRuleTest.php +++ b/tests/Validation/ValidationNumericRuleTest.php @@ -345,5 +345,4 @@ public function testUniquenessValidation() $rule = Rule::numeric()->integer()->digits(2)->size(2); $this->assertEquals('numeric|integer|digits:2|size:2', (string) $rule); } - }