diff --git a/README.md b/README.md
index d417b8c..eb5700f 100644
--- a/README.md
+++ b/README.md
@@ -96,25 +96,22 @@ You may also utilize other place-holders in validation messages. For example the
##### Available rules
-`between:min,max`:
-```
-Validates that a cell value is between a :min and :max. The rule exposes the :min and :max placeholder for inline messages
-```
-`ascii_only`:
-```
-Validates that a cell value does not contain a non-ascii character
-```
-`url`:
-```
-Validates that a cell value is a valid URL. By valid URL we mean
-
-(#protocol)
-(#basic auth)
-(#a domain name or #an IP address or #an IPv6 address)
-(#a port(optional)) then
-(#a /, nothing, a / with something, a query or a fragment)
-
-```
+| Rule | Description |
+| --- | --- |
+| `alpha` | Validates that a cell value contains only lower and upper case alphabets |
+| `alpha_num` | Validates that a cell value contains only numbers, lower and upper case alphabets |
+| `ascii_only` | Validates that a cell value does not contain a non-ascii character |
+| `between:min,max` | Validates that a cell value is between a :min and :max. The rule exposes the :min and :max placeholder for inline messages |
+| `in:foo,bar,baz...` | Validates that a cell value is one of the values specified by the rule |
+| `integer` | Validates that a cell has an integer value |
+| `max:max_val` | Validates that a cell value must be less than or equal to max_val. |
+| `max_length:length` | Validates that a cell value must contain less than or equal to length characters. |
+| `min:min_val` | Validates that a cell value must be greater than or equal to min_val. |
+| `min_length:length` | Validates that a cell value must contain greater than or equal to length characters. |
+| `numeric` | Validates that a cell has a numeric value |
+| `required` | Validates that a cell cannot be empty |
+| `required_if:other_column,other_column_val` | Validates that a cell value must be present if a column specified by other_column has the value as other_column_val |
+| `url` | Validates that a cell value is a valid URL. By valid URL we mean
(#protocol)
(#basic auth)
(#a domain name or #an IP address or #an IPv6 address)
(#a port(optional)) then
(#a /, nothing, a / with something, a query or a fragment) |
##### Writing CSV Output Data
diff --git a/src/Contracts/ArrayParameterizedRuleInterface.php b/src/Contracts/ArrayParameterizedRuleInterface.php
new file mode 100644
index 0000000..e13ac04
--- /dev/null
+++ b/src/Contracts/ArrayParameterizedRuleInterface.php
@@ -0,0 +1,22 @@
+replaceParameterPlaceholder(
+ $message,
+ $rule->allowedParameters(),
+ $rule->parseParameterValues($parameters)
+ );
+ }
+
$message = $this->replaceValuePlaceholder($message, $value);
$message = $this->replaceErrorLinePlaceholder($message, $lineNumber);
diff --git a/src/Rules/Alpha.php b/src/Rules/Alpha.php
new file mode 100644
index 0000000..eab95fa
--- /dev/null
+++ b/src/Rules/Alpha.php
@@ -0,0 +1,35 @@
+= (int) $min && $size <= (int) $max;
+ return +$value >= +$min && +$value <= +$max;
}
/**
@@ -33,6 +31,6 @@ public function passes($value, array $parameters): bool
*/
public function message(): string
{
- return 'The :attribute value :value is not between :min - :max on line :line.';
+ return 'The :attribute value :value must be between :min and :max on line :line.';
}
}
diff --git a/src/Rules/ClosureValidationRule.php b/src/Rules/ClosureValidationRule.php
index f54b9f7..40d2080 100755
--- a/src/Rules/ClosureValidationRule.php
+++ b/src/Rules/ClosureValidationRule.php
@@ -42,11 +42,11 @@ public function __construct(\Closure $callback)
*
* @param mixed $value
*/
- public function passes($value, array $parameters): bool
+ public function passes($value, array $parameters, array $row): bool
{
$this->failed = false;
- $this->callback->__invoke($value, function ($message) {
+ $this->callback->__invoke($value, $row, function ($message) {
$this->failed = true;
$this->message = $message;
diff --git a/src/Rules/In.php b/src/Rules/In.php
new file mode 100644
index 0000000..5193bfd
--- /dev/null
+++ b/src/Rules/In.php
@@ -0,0 +1,65 @@
+= +$min;
+ }
+
+ /**
+ * Get the validation error message.
+ */
+ public function message(): string
+ {
+ return 'The :attribute value :value may not be less than :min on line :line.';
+ }
+}
diff --git a/src/Rules/MinLength.php b/src/Rules/MinLength.php
new file mode 100644
index 0000000..c65d9f6
--- /dev/null
+++ b/src/Rules/MinLength.php
@@ -0,0 +1,36 @@
+= $length;
+ }
+
+ /**
+ * Get the validation error message.
+ */
+ public function message(): string
+ {
+ return 'The :attribute value :value may not have less than :length characters on line :line.';
+ }
+}
diff --git a/src/Rules/Numeric.php b/src/Rules/Numeric.php
new file mode 100644
index 0000000..85db62e
--- /dev/null
+++ b/src/Rules/Numeric.php
@@ -0,0 +1,35 @@
+filePath, 'r'))) {
while (false !== ($row = fgetcsv($handle, 0, $this->delimiter))) {
++$this->currentRowLineNumber;
+
+ if ($this->shouldTrim) {
+ $row = array_map('trim', $row);
+ }
+
if (empty($this->headers)) {
$this->setHeaders($row);
continue;
@@ -277,7 +290,7 @@ protected function validateAttribute(string $attribute, $rule): void
if ($this->isValidateAble($rule, $parameters)) {
$ruleClass = $this->getRuleClass($rule);
- if (!$ruleClass->passes($value, $parameters)) {
+ if (!$ruleClass->passes($value, $parameters, $this->currentRow)) {
$this->addFailure(
$this->getMessage($attribute, $ruleClass, $rule),
$attribute,
@@ -357,6 +370,13 @@ protected function passesParameterCheck($rule, array $parameters): bool
return $parameterCount === $ruleParameterCount;
}
+ if ($rule instanceof ArrayParameterizedRuleInterface) {
+ $ruleParameterCount = count($rule->allowedParameters());
+ $parameterCount = count($parameters);
+
+ return $parameterCount >= $ruleParameterCount;
+ }
+
return true;
}
@@ -371,7 +391,7 @@ protected function validateUsingCustomRule(
array $parameters,
ValidationRuleInterface $rule
): void {
- if (!$rule->passes($value, $parameters)) {
+ if (!$rule->passes($value, $parameters, $this->currentRow)) {
$this->addFailure($rule->message(), $attribute, $value, $rule, $parameters);
}
}
@@ -407,4 +427,15 @@ protected function getValue(string $attribute)
{
return $this->currentRow[$attribute];
}
+
+ /**
+ * Set if cell values should be trimmed
+ * before validation.
+ *
+ * @return void
+ */
+ public function setShouldTrim(bool $shouldTrim = false)
+ {
+ $this->shouldTrim = $shouldTrim;
+ }
}
diff --git a/tests/data/alpha_num_test.csv b/tests/data/alpha_num_test.csv
new file mode 100644
index 0000000..594214f
--- /dev/null
+++ b/tests/data/alpha_num_test.csv
@@ -0,0 +1,2 @@
+name,address,stars,contact,uri
+ Well Health Hotels ,Inga N. P.O. Box 567,3,,http//:well.org
diff --git a/tests/data/in_test.csv b/tests/data/in_test.csv
new file mode 100644
index 0000000..d8c7e4f
--- /dev/null
+++ b/tests/data/in_test.csv
@@ -0,0 +1,3 @@
+name,address,stars,contact,uri
+Well Health Hotels,Inga N. P.O. Box 567,2,Kasper Zen,http//:well.org
+Bad Health Hotels,Inga N. P.O. Box 568,2,,http//:well.org
diff --git a/tests/data/integer_test.csv b/tests/data/integer_test.csv
new file mode 100644
index 0000000..b80dc05
--- /dev/null
+++ b/tests/data/integer_test.csv
@@ -0,0 +1,2 @@
+id,name,address,stars,contact,uri
+,Well Health Hotels,Inga N. P.O. Box 567,5.5,Kasper Zen,http//:well.org
diff --git a/tests/data/min_max_test.csv b/tests/data/min_max_test.csv
new file mode 100644
index 0000000..d868f8a
--- /dev/null
+++ b/tests/data/min_max_test.csv
@@ -0,0 +1,2 @@
+name,address,stars,contact,uri
+Well Health Hotels,Inga N. P.O. Box 567,3,Kasper Zen,http//:well.org
diff --git a/tests/data/numeric_test.csv b/tests/data/numeric_test.csv
new file mode 100644
index 0000000..4084d72
--- /dev/null
+++ b/tests/data/numeric_test.csv
@@ -0,0 +1,2 @@
+id,name,address,stars,contact,uri
+,Well Health Hotels,Inga N. P.O. Box 567,A,Kasper Zen,http//:well.org
diff --git a/tests/data/required_if_test.csv b/tests/data/required_if_test.csv
new file mode 100644
index 0000000..a0e853f
--- /dev/null
+++ b/tests/data/required_if_test.csv
@@ -0,0 +1,3 @@
+name,address,stars,contact,uri
+Well Health Hotels,Inga N. P.O. Box 567,3,,http//:well.org
+Bad Health Hotels,Inga N. P.O. Box 568,2,,http//:well.org
diff --git a/tests/data/required_test.csv b/tests/data/required_test.csv
new file mode 100644
index 0000000..9158c03
--- /dev/null
+++ b/tests/data/required_test.csv
@@ -0,0 +1,2 @@
+name,address,stars,contact,uri
+Well Health Hotels,,3,Kasper Zen,http//:well.org
diff --git a/tests/src/CsvValidatorTest.php b/tests/src/CsvValidatorTest.php
index 3422405..eede04b 100755
--- a/tests/src/CsvValidatorTest.php
+++ b/tests/src/CsvValidatorTest.php
@@ -36,6 +36,64 @@ public function testInvalidCsvFilePath()
);
}
+ public function testAlphaValidationRule()
+ {
+ $file = $this->testAssets . '/alpha_num_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'name' => ['alpha'],
+ 'contact' => ['alpha'],
+ ]);
+
+ $validator->setShouldTrim(true);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The name value Well Health Hotels may only contain letters on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testAlphaNumValidationRule()
+ {
+ $file = $this->testAssets . '/alpha_num_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'address' => ['alpha_num'],
+ 'contact' => ['alpha_num'],
+ ]);
+
+ $validator->setShouldTrim(true);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The address value Inga N. P.O. Box 567 may only contain letters and numbers on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
public function testAsciiOnlyValidationRule()
{
$file = $this->testAssets . '/ascii_test.csv';
@@ -83,17 +141,18 @@ public function testBetweenValidationRule()
);
$this->assertContains(
- 'The stars value 3 is not between 4 - 10 on line 2.',
+ 'The stars value 3 must be between 4 and 10 on line 2.',
$validator->errors()['data'][0]['errors']
);
}
- public function testUrlValidationRule()
+ public function testInValidationRule()
{
- $file = $this->testAssets . '/url_test.csv';
+ $file = $this->testAssets . '/in_test.csv';
$validator = new Validator($file, ',', [
- 'uri' => ['url'],
+ 'stars' => ['in:3,5,8,10'],
+ 'contact' => ['in:Kasper Zen'],
]);
$this->assertTrue($validator->fails());
@@ -103,28 +162,252 @@ public function testUrlValidationRule()
$validator->errors()['message']
);
- $validationErrors = $validator->errors();
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The stars value 2 does not exist in 3,5,8,10 on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testIntegerValidationRule()
+ {
+ $file = $this->testAssets . '/integer_test.csv';
- for ($csvRow = 0; $csvRow < 3; ++$csvRow) {
- $this->assertArrayHasKey(
- 'errors',
- $validationErrors['data'][$csvRow]
- );
- }
+ $validator = new Validator($file, ',', [
+ 'stars' => ['integer'],
+ 'id' => ['integer'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
$this->assertContains(
- 'The uri value http//:well.org is not a valid url on line 2.',
- $validationErrors['data'][0]['errors']
+ 'The stars value 5.5 must be an integer on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testMaxValidationRule()
+ {
+ $file = $this->testAssets . '/min_max_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'stars' => ['max:1'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
);
$this->assertContains(
- 'The uri value is not a valid url on line 3.',
- $validationErrors['data'][1]['errors']
+ 'The stars value 3 may not be greater than 1 on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testMaxLengthValidationRule()
+ {
+ $file = $this->testAssets . '/min_max_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'name' => ['max_length:15'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
);
$this->assertContains(
- 'The uri value is not a valid url on line 4.',
- $validationErrors['data'][2]['errors']
+ 'The name value Well Health Hotels may not have more than 15 characters on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testMinValidationRule()
+ {
+ $file = $this->testAssets . '/min_max_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'stars' => ['min:4'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The stars value 3 may not be less than 4 on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testMinLengthValidationRule()
+ {
+ $file = $this->testAssets . '/min_max_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'name' => ['min_length:20'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The name value Well Health Hotels may not have less than 20 characters on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testNumericValidationRule()
+ {
+ $file = $this->testAssets . '/numeric_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'stars' => ['numeric'],
+ 'id' => ['numeric'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The stars value A must be a number on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testRequiredIfValidationRule()
+ {
+ $file = $this->testAssets . '/required_if_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'contact' => ['required_if:address,Inga N. P.O. Box 567'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The contact field is required when address is Inga N. P.O. Box 567 on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testRequiredValidationRule()
+ {
+ $file = $this->testAssets . '/required_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'address' => ['required'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ 'The address value is required on line 2.',
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testUrlValidationRule()
+ {
+ $file = $this->testAssets . '/url_test.csv';
+
+ $validator = new Validator($file, ',', [
+ 'uri' => ['url'],
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $validationErrors = $validator->errors();
+
+ $this->assertArrayHasKey(
+ 'errors',
+ $validationErrors['data'][0]
+ );
+
+ $this->assertContains(
+ 'The uri value http//:well.org is not a valid url on line 2.',
+ $validationErrors['data'][0]['errors']
);
}
@@ -159,7 +442,7 @@ public function testValidatorWithCustomRuleClosure()
$file = $this->testAssets . '/url_test.csv';
$validator = new Validator($file, ',', [
- 'uri' => [function ($value, $fail) {
+ 'uri' => [function ($value, $row, $fail) {
if (0 !== strpos($value, 'https://')) {
return $fail('The URL passed must be https i.e it must start with https://');
}
diff --git a/tests/src/UppercaseRule.php b/tests/src/UppercaseRule.php
index 47a20b9..5af9ba9 100644
--- a/tests/src/UppercaseRule.php
+++ b/tests/src/UppercaseRule.php
@@ -8,9 +8,8 @@ class UppercaseRule implements ValidationRuleInterface
{
/**
* @param mixed $value
- * @param $parameters
*/
- public function passes($value, array $parameters): bool
+ public function passes($value, array $parameters, array $row): bool
{
return strtoupper($value) === $value;
}