diff --git a/.gitignore b/.gitignore
index dabaaa6..ed7de56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
.idea/*
vendor
build
-test.xml
-test.json
-/sample.xml
-/sample.json
+composer.lock
+sample.json
+sample.xml
+valid_test.json
+valid_test.xml
diff --git a/.travis.yml b/.travis.yml
index 79b7bdb..7d0a386 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,8 @@ install:
- composer install --no-interaction
script:
- - phpunit
+ - composer test
after_success:
- - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
+ - bash <(curl -s https://codecov.io/bash)
+
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index af8ac5c..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 Oforomeh Oshomo
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
index 43e8398..4fdb35c 100644
--- a/README.md
+++ b/README.md
@@ -1,180 +1,292 @@
-
-
-
-
-
-
-
-# CSVUtils by Oforomeh Oshomo
-
-### How to run
-
-From the package root, run `composer install` then using php built in server run `php -S localhost:8000`, this would start the server at `localhost:8000`. Visit the URL from your browser and you should see the generated files at the root of the package.
-The `sample.csv` is at the root of the package. Generated files would also be saved there.
-
-### Implementation
-
-The `CsvConverter` and `CsvValidator` were written in isolation of each other so, that they can be used seperately if need arise. The `CsvConverter` expects a valid file path and an array of validation rule(s) and in turn an array of all, valid and invalid data can be accessed.
-
-The `CsvValidator` on the other hand expects a valid folder path, array of data to be written into either `JSON` or `XML`, and in turn write the file in the folder specified.
-
-### Documentation
-
-#### CsvValidator
-
-Currently supported validation rules:
-
-`min (expects a value)`:
-```
-Validates that a cell value is not less than the specified value
-```
-`max (expects a value)`:
-```
-Validates that a cell value is not greater than the specified value
-```
-`ascii`:
-```
-Validates that a cell value does not contain a non-ascii character
-```
-`url`:
-```
-Validates that a cell 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)
-
-```
-
-Initializing a `CsvValidator`. Set a valid csv file path and pass in your validation rules.
-```php
-$validator = new CsvValidator("some/valid/file_path", [
- "name" => ["ascii" => ""],
- "uri" => ["url" => ""],
- "stars" => ["min" => 0, "max" => 5]
-]);
-```
-
-OR
-
-```php
-$validator = new CsvValidator();
-$validator->setFilePath("some/valid/file_path");
-$validator->setRules([
- "uri" => ["url" => ""],
- "stars" => ["min" => 0, "max" => 5]
-]);
-```
-
-Validating the CSV
-
-```php
-$validator->validate();
-```
-
-Other available methods (To be called after `validate()` else you would get an empty array `[]`)
-
-`getHeaders()` e.g. `$validator->vaildate()->getHeaders();`
-Returns CSV header as an array
-
-`getAllData()` e.g. `$validator->vaildate()->getAllData();`
-Returns All CSV data both those that passed validation and those that failed validation as an array
-
-`getValidData()` e.g. `$validator->vaildate()->getValidData();`
-Returns All CSV data that passed validation as an array
-
-`getInvalidData()` e.g. `$validator->vaildate()->getInvalidData();`
-Returns All CSV data that failed validation as an array with `error` bag containing which and why each column failed.
-
-#### CsvConverter
-
-Currently supported converters:
-
-`JSON` and `XML`
-
-Initializing a `CsvConverter`. The `CsvConverter` take two parameters, an array of data and an optional valid folder path if you want the data to be written to a file. The methods below from `CsvValidator` returns array of data, so they can be passed as parameter to `CsvConverter`.
-
-`$validator->validate()->getValidData();`,
-`$validator->validate()->getInvalidData();`
-`$validator->validate()->getAllData();`
-
-```php
-$data = [
- [
- 'name' => 'Beni Gold Hotel and Apartments'
- 'stars' => '5'
- 'uri' => 'https://hotels.ng/hotel/86784-benigold-hotel-lagos'
- ],
- [
- 'name' => 'Hotel Ibis Lagos Ikeja'
- 'stars' => '3'
- 'uri' => 'https://hotels.ng/hotel/52497-hotel-ibis-lagos-ikeja-lagos'
- ],
- [
- 'name' => 'Silver Grandeur Hotel'
- 'stars' => '7'
- 'uri' => 'https://hotels.ng/hotel/88244-silver-grandeur-hotel-lagos'
- ],
- [
- 'name' => 'Limeridge Hotel, Lekki'
- 'stars' => '7'
- 'uri' => 'https://hotels.ng/hotel/65735-limeridge-hotel-lagos'
- ]
-]
-$converter = new CsvConverter($data);
-```
-
-OR
-
-```php
-$converter = new CsvConverter($data, "some/valid/folder");
-```
-
-OR
-
-```php
-$converter = new CsvConverter();
-$converter->setPath("some/valid/folder");
-$converter->setData([]);
-```
-
-If you specify a filename for `toJSon`, `toXml` or any other supported methods, then you must initialize `CsvConverter` with a valid folder path else an exception would be thrown.
-
-Omitting the filename would return the data as either JSON, XML or any other supported file extensions irrespective of whether `CsvConverter` was initialized with a valid folder path or not
-
-Converting to `JSON`.
-
-```php
-$converter->toJSon("filename.json");
-```
-
-OR
-
-```php
-header('Content-Type: application/json');
-print($converter->toJSon());
-```
-
-Converting to `XML`
-```php
-$converter->toXml("filename.xml");
-```
-
-OR
-
-```php
-header('Content-Type: application/xml');
-print($converter->toXml());
-```
-
-### Running Test's
-
-Run `phpunit` from the root of the Package. This assumes you have ran `composer install`.
-
-### Todo's
-
- - Support for more validation rules `string`, `number`, `date`, `required`, `boolean`, `email`, `phone_number`, `ip_address`
- - Make CsvConverter extensible, so that user can pass there own validation rule
- - Add options to sort/group/filter the data before writing the data.
+
+
+
+
+
+
+
+# CSVUtils
+
+*Make sure you use a tagged version when requiring this package.*
+
+### How to run
+
+I have added a sample `index.php` file for a quick test of how to use the package. To run the sample; from the package root, run `composer install` then using php built in server run `php -S localhost:8000`, this would start the server at `localhost:8000`. Visit the URL from your browser and you should see the generated files in the `sample` folder at the root of the package.
+
+### Implementation
+
+The `Validator` expects a valid file path, the CSV delimiter, an array of validation rule(s) and an optional message(s) array to over-write the default messages of the validator.
+
+### Documentation
+
+##### Initializing a `Validator`.
+
+Set a valid csv file path, pass the CSV delimiter and pass in your validation rules.
+
+```php
+use Oshomo\CsvUtils\Validator\Validator;
+
+$validator = new Validator("some/valid/file_path", ",", [
+ "name" => ["ascii_only"],
+ "uri" => ["url"],
+ "stars" => ["between:0,5"]
+]);
+```
+
+##### Validating the CSV
+
+Now we are ready to validate the CSV. The validator provides a `validate ` method that can be called like so: `$validator->validate();`. The `validate` method returns an array of the invalid rows if validation fails. If the validation passes the `validate` method returns the CSV data as an array
+
+A better implementation:
+
+```php
+use Oshomo\CsvUtils\Validator\Validator;
+
+$validator = new Validator("some/valid/file_path", ",", [
+ 'title' => ["ascii_only", "url"]
+]);
+
+if ($validator->fails()) {
+ // Do something when validation fails
+ $errors = $validator->errors()
+}
+```
+
+##### Error messages
+
+To get the rows with validation errors and there errors. The validator expose `errors` method that can be used like so `$validator->errors()`.
+
+You can also customize the error messages for different validation rules and different attributes by passing a message array to the validator like so:
+
+```php
+use Oshomo\CsvUtils\Validator\Validator;
+
+$validator = new Validator("some/valid/file_path", ",", [
+ 'title' => ["ascii_only", "url"]
+], [
+ 'ascii_only' => 'The :value supplied for :attribute attribute is invalid',
+ // This specifies a custom message for a given attribute.
+ 'hotel_link:url' => 'The :attribute must be a valid link',
+]);
+```
+
+In this above example, the `:attribute` place-holder will be replaced by the actual name of the field under validation. The `:value` place-holder will also be replaced with value being validated. You may also utilize other place-holders in validation messages. For example the `between` rule exposes two other placeholder `min` and `max`. Find more about this in the available rules section
+
+##### 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)
+
+```
+
+##### Writing CSV output data
+
+The output of the CSV file can be written into any format. The currently suported format is `xml` and `json`. The validator exposes a `write` method to write the output data into the same folder as the CSV. Find example implementation below:
+
+```php
+use Oshomo\CsvUtils\Validator\Validator;
+use Oshomo\CsvUtils\Converter\JsonConverter;
+use Oshomo\CsvUtils\Converter\XmlConverter;
+
+$validator = new Validator('some/valid/file_path', ',', [
+ "stars" => ["between:0,5"],
+ "name" => ["ascii_only"],
+ "uri" => ["url"],
+]);
+
+if(!$validator->fails()) {
+ $validator->write(new JsonConverter());
+ $validator->write(new XmlConverter("hotel"));
+} else {
+ print_r($validator->errors());
+}
+```
+
+The `JsonConverter` simply writes the output data as JSON. The `XmlConverter` converter writes the data as XML. `XmlConverter` takes an optional parameter for setting the XML records element. If non is supplied it defaults to `item` e.g `$validator->write(new XmlConverter("hotel"));` would write the below:
+
+```
+
+
+
+ Beni Gold Hotel and Apartments
+ 5
+ https://hotels.ng/hotel/86784-benigold-hotel-lagos
+
+
+ Hotel Ibis Lagos Ikeja
+ 4
+ https://hotels.ng/hotel/52497-hotel-ibis-lagos-ikeja-lagos
+
+
+```
+
+**NOTE**: *Either validation passes or fails, you can always write the CSV output data to the available formats. In cases where validation fails there would be an extra error property in the written data.*
+
+##### Passing custom rule to validator
+
+Passing a custom rule to the validartor is easy. Create a CustomRule class the implements `Oshomo\CsvUtils\Contracts\ValidationRuleInterface` interface. And pass that class to the rule array, easy. E.g:
+
+```php
+use Oshomo\CsvUtils\Validator\Validator;
+
+$validator = new Validator('some/valid/file_path', ',', [
+ "name" => ["ascii_only", new UppercaseRule]
+]);
+```
+
+The class definition for `UppercaseRule`. Follow the same approach if you want to create your own rule.
+
+```php
+use Oshomo\CsvUtils\Contracts\ValidationRuleInterface;
+
+class UppercaseRule implements ValidationRuleInterface
+{
+ /**
+ * Determine if the validation rule accepts parameters or not.
+ * If it does not accept any parameter return true else return false
+ *
+ * @return boolean
+ */
+ public function isImplicit()
+ {
+ return true;
+ }
+
+ /**
+ * Get the number of parameters that should be supplied.
+ * If no parameter should be supplied return 0 else
+ * return the number of parameters that should be returned
+ *
+ * @return integer
+ */
+ public function parameterCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Determines if the validation rule passes. This is where we do the
+ * actual validation. If the validation passes return true else false
+ *
+ * @param mixed $value
+ * @param $parameters
+ * @return bool
+ */
+ public function passes($value, $parameters)
+ {
+ return strtoupper($value) === $value;
+ }
+
+ /**
+ * Get the validation error message. Specify the message that should
+ * be returned if the validation fails. You can make use of the
+ * :attribute and :value placeholders in the message string
+ *
+ * @return string
+ */
+ public function message()
+ {
+ return "The :attribute value :value must be uppercase.";
+ }
+
+ /**
+ * Replace error messages parameter with right values. If you want
+ * to allow user pass custom placeholders in the inline message
+ * specify and replace them here. If not just return $message
+ * i.e return $message. But if you want to allow custom placeholder
+ * return str_replace(
+ * [':custom_a', ':custom_b'],
+ * [$parameters[0], $parameters[1]],
+ * $message
+ * );
+ *
+ * @param string $message
+ * @param array $parameters
+ * @return string
+ */
+ public function parameterReplacer($message, $parameters)
+ {
+ return $message;
+ }
+}
+
+```
+
+##### Writing CSV output data to other formats
+
+Writing the CSV ouput data to other format is also very easy. Create a CustomConverter class the implements `Oshomo\CsvUtils\Contracts\ConverterHandlerInterface` interface. And pass that class to the `write` method of the validator, easy. Below is an sample implementation of a JSON converter
+
+```php
+use Oshomo\CsvUtils\Contracts\ConverterHandlerInterface;
+
+class JsonConverter implements ConverterHandlerInterface
+{
+ const FILE_EXTENSION = "json";
+
+ /**
+ * The converted data
+ *
+ * @var string
+ */
+ protected $data;
+
+ /**
+ * @return string
+ */
+ public function getExtension()
+ {
+ return self::FILE_EXTENSION;
+ }
+
+ /**
+ * @param array $data
+ * @return $this|mixed
+ */
+ public function convert($data)
+ {
+ $this->data = json_encode($data,
+ JSON_PRETTY_PRINT |
+ JSON_NUMERIC_CHECK |
+ JSON_UNESCAPED_SLASHES |
+ JSON_UNESCAPED_UNICODE
+ );
+
+ return $this;
+ }
+
+ /**
+ * @param string $filename
+ * @return bool
+ */
+ public function write($filename)
+ {
+ return (file_put_contents($filename, $this->data)) ? true : false;
+ }
+}
+
+//////////////////////////////////////////////////////
+// To use the converter above.
+//////////////////////////////////////////////////////
+
+$validator->write(new JsonConverter());
+
+```
+
+### Running Test's
+
+Run `composer test` from the root of the Package.
diff --git a/composer.json b/composer.json
old mode 100644
new mode 100755
index 221d2a4..3019bbd
--- a/composer.json
+++ b/composer.json
@@ -1,29 +1,34 @@
{
- "name": "oshomo/csv-utils",
- "description": "A CSV utility to read, validate and write data to multiple format including JSON, XML etc.",
- "type": "library",
- "license": "LGPL-3.0+",
- "authors": [
- {
- "name": "Oshomo Oforomeh",
- "email": "oshomo.oforomeh2010@gmail.com"
- }
- ],
- "minimum-stability": "dev",
- "require": {
- "php": "^5.6"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.7"
- },
- "autoload": {
- "psr-4": {
- "Oshomo\\CsvUtils\\": "src/"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "Oshomo\\CsvUtils\\Tests\\": "tests/"
- }
+ "name": "oshomo/csv-utils",
+ "description": "A CSV utility to read, validate and write data to multiple format including JSON, XML etc.",
+ "type": "library",
+ "authors": [
+ {
+ "name": "Oshomo Oforomeh",
+ "email": "oshomo.oforomeh2010@gmail.com"
}
+ ],
+ "minimum-stability": "dev",
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7"
+ },
+ "autoload": {
+ "psr-4": {
+ "Oshomo\\CsvUtils\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Oshomo\\CsvUtils\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "phpunit": "phpunit",
+ "test": [
+ "@phpunit"
+ ]
+ }
}
diff --git a/index.php b/index.php
index f07497f..bdcd00d 100644
--- a/index.php
+++ b/index.php
@@ -1,27 +1,23 @@
["ascii" => ""],
- "uri" => ["url" => ""],
- "stars" => ["min" => 7, "max" => 10]
+$file = $file_path . "/sample/sample.csv";
+$validator = new Validator($file, ',', [
+ "stars" => ["between:0,5"],
+ "name" => ["ascii_only"],
+ "uri" => ["url"],
]);
-try {
- $data = $validator->validate()->getValidData();
- if($data) {
- $converter = new CsvConverter($data, $file_path);
- echo $converter->toJson("sample.json") . "\n";
- echo $converter->toXml("sample.xml");
- }
-}catch (Exception $exception) {
- echo $exception->getMessage();
+if(!$validator->fails()) {
+ $validator->write(new JsonConverter());
+ $validator->write(new XmlConverter());
+} else {
+ $validator->write(new JsonConverter());
+ $validator->write(new XmlConverter('hotel'));
+ print_r($validator->errors());
}
-
diff --git a/phpunit.xml b/phpunit.xml
old mode 100644
new mode 100755
index c0545fa..ea735c9
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -33,4 +33,4 @@
-
\ No newline at end of file
+
diff --git a/sample.csv b/sample/sample.csv
similarity index 100%
rename from sample.csv
rename to sample/sample.csv
diff --git a/src/Contracts/ConverterHandlerInterface.php b/src/Contracts/ConverterHandlerInterface.php
new file mode 100755
index 0000000..1b8eebc
--- /dev/null
+++ b/src/Contracts/ConverterHandlerInterface.php
@@ -0,0 +1,32 @@
+setPath($path);
- }
-
- $this->data = $data;
- }
-
- /**
- * Get full file path
- *
- * @param $filename
- * @return string
- */
- private function fullFilePath($filename)
- {
- $fullFilePath = "";
-
- if (!empty($this->path) && !empty($filename)) {
- $fullFilePath = $this->path . DIRECTORY_SEPARATOR . $filename;
- }
-
- return $fullFilePath;
- }
-
- /**
- * @param BaseConverter $converter
- * @param $filename
- * @return mixed
- */
- private function convert(BaseConverter $converter, $filename)
- {
- if (!empty($filename) && empty($this->getPath())) {
- throw new InvalidArgumentException("You must initialize the converter with a valid path");
- }
-
- return $converter->convert($this->data)->write($this->fullFilePath($filename));
- }
-
- /**
- * Determine if the given path is a directory.
- *
- * @param string $directory
- * @return bool
- */
- private function isDirectory($directory)
- {
- if (!is_dir($directory)) {
- throw new InvalidArgumentException("The path supplied is not a directory");
- }
- }
-
- /**
- * @return string
- */
- public function getPath()
- {
- return $this->path;
- }
-
- /**
- * @param string $path
- * @return $this
- */
- public function setPath($path)
- {
- $this->path = $path;
-
- $this->isDirectory($path);
-
- return $this;
- }
-
- /**
- * @return string
- */
- public function getData()
- {
- return $this->data;
- }
-
- /**
- * @param string $data
- * @return $this
- */
- public function setData($data)
- {
- $this->data = $data;
-
- return $this;
- }
-
- /**
- * Convert Array to JSON
- * @param $filename
- * @return mixed
- */
- public function toJson($filename = "")
- {
- return $this->convert(new JsonConverter(), $filename);
-
- }
-
- /**
- * Convert Array to XML
- * @param $filename
- * @return mixed
- */
- public function toXml($filename = "")
- {
- return $this->convert(new XmlConverter(), $filename);
-
- }
-
-}
\ No newline at end of file
diff --git a/src/Converter/JsonConverter.php b/src/Converter/JsonConverter.php
old mode 100644
new mode 100755
index b6f99d1..d438eeb
--- a/src/Converter/JsonConverter.php
+++ b/src/Converter/JsonConverter.php
@@ -1,39 +1,51 @@
data = json_encode($data, JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
+ $this->data = json_encode($data,
+ JSON_PRETTY_PRINT |
+ JSON_NUMERIC_CHECK |
+ JSON_UNESCAPED_SLASHES |
+ JSON_UNESCAPED_UNICODE
+ );
+
return $this;
}
+ /**
+ * @param string $filename
+ * @return bool
+ */
public function write($filename)
{
- if (empty($filename)) {
- return $this->data;
- } else {
- if (!file_put_contents($filename, $this->data)){
- return "Data to JSON conversion not successful.";
- } else {
- return "Data to JSON conversion successful. Check {$filename} for your file.";
- }
- }
+ return (file_put_contents($filename, $this->data)) ? true : false;
}
-}
\ No newline at end of file
+}
diff --git a/src/Converter/XmlConverter.php b/src/Converter/XmlConverter.php
old mode 100644
new mode 100755
index 5ec3d1a..c846850
--- a/src/Converter/XmlConverter.php
+++ b/src/Converter/XmlConverter.php
@@ -1,70 +1,101 @@
recordElement = $recordElement;
+ }
+
$this->data = new SimpleXMLElement('');
}
- private function toXml($data, $xml_data) {
+ /**
+ * @return string
+ */
+ public function getExtension()
+ {
+ return self::FILE_EXTENSION;
+ }
+
+ /**
+ * @param $data
+ * @param $xmlData
+ */
+ protected function toXml($data, $xmlData) {
foreach( $data as $key => $value ) {
if( is_numeric($key) ){
- $key = 'item'.$key; //dealing with <0/>.. issues
+ $key = $this->recordElement;
}
if( is_array($value) ) {
- $sub_node = $xml_data->addChild($key);
- $this->toXml($value, $sub_node);
+ $subNode = $xmlData->addChild($key);
+ $this->toXml($value, $subNode);
} else {
- $xml_data->addChild("$key",htmlspecialchars("$value"));
+ $xmlData->addChild("$key",htmlspecialchars("$value"));
}
}
}
+ /**
+ * @param $data
+ * @return $this|mixed
+ */
public function convert($data)
{
$this->toXml($data, $this->data);
+
return $this;
}
+ /**
+ * @param $filename
+ * @return bool
+ */
public function write($filename)
{
- if (empty($filename)) {
- return $this->data->asXML();
- } else {
- // Use DOMDocument to beautify the SimpleXML output
- $dom = new DOMDocument('1.0');
- $dom->preserveWhiteSpace = false;
- $dom->formatOutput = true;
- $dom_xml = dom_import_simplexml($this->data);
- $dom_xml = $dom->importNode($dom_xml, true);
- $dom_xml = $dom->appendChild($dom_xml);
-
- if (!$dom->save($filename)) {
- return "Data to XML conversion not successful.";
- } else {
- return "Data to XML conversion successful. Check {$filename} for your file.";
- }
- }
+ $dom = new DOMDocument('1.0');
+
+ $dom->preserveWhiteSpace = false;
+
+ $dom->formatOutput = true;
+
+ $domXml = dom_import_simplexml($this->data);
+
+ $domXml = $dom->importNode($domXml, true);
+
+ $dom->appendChild($domXml);
+ return ($dom->save($filename)) ? true : false;
}
-}
\ No newline at end of file
+}
diff --git a/src/Exceptions/InvalidRuleDeclarationException.php b/src/Exceptions/InvalidRuleDeclarationException.php
deleted file mode 100644
index bca2545..0000000
--- a/src/Exceptions/InvalidRuleDeclarationException.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getInlineMessage($attribute, $actualRule);
+
+ if (! is_null($inlineMessage)) {
+ return $inlineMessage;
+ }
+
+ return $rule->message();
+ }
+
+ /**
+ * Get the proper inline error message passed to the validator
+ *
+ * @param string $attribute
+ * @param string $rule
+ * @return string|null
+ */
+ protected function getInlineMessage($attribute, $rule)
+ {
+ return $this->getFromLocalArray($attribute, $this->ruleToLower($rule));
+ }
+
+ /**
+ * Get the inline message for a rule if it exists.
+ *
+ * @param string $attribute
+ * @param string $lowerRule
+ * @return string|null
+ */
+ protected function getFromLocalArray($attribute, $lowerRule)
+ {
+ $source = $this->customMessages;
+
+ $keys = ["{$attribute}.{$lowerRule}", $lowerRule];
+
+ foreach ($keys as $key) {
+ foreach (array_keys($source) as $sourceKey) {
+ if ($sourceKey === $key) {
+ return $source[$sourceKey];
+ }
+ }
+ }
+ }
+
+ /**
+ * @param $rule
+ * @return null|string|string[]
+ */
+ protected function ruleToLower($rule)
+ {
+ $lowerRule = preg_replace('/[A-Z]/', '_$0', $rule);
+
+ $lowerRule = strtolower($lowerRule);
+
+ $lowerRule = ltrim($lowerRule, '_');
+
+ return $lowerRule;
+ }
+
+ /**
+ * Replace all error message place-holders with actual values.
+ *
+ * @param string $message
+ * @param string $attribute
+ * @param mixed $value
+ * @param \Oshomo\CsvUtils\Contracts\ValidationRuleInterface $rule
+ * @param array $parameters
+ * @return string
+ */
+ protected function makeReplacements($message, $attribute, $value, $rule, $parameters)
+ {
+ $message = $this->replaceAttributePlaceholder($message, $attribute);
+
+ $message = $this->replaceValuePlaceholder($message, $value);
+
+ $message = $rule->parameterReplacer($message, $parameters);
+
+ return $message;
+ }
+
+ /**
+ * Replace the :attribute placeholder in the given message.
+ *
+ * @param string $message
+ * @param string $attribute
+ * @return string
+ */
+ protected function replaceAttributePlaceholder($message, $attribute)
+ {
+ return str_replace([':attribute'], [$attribute], $message);
+ }
+
+ /**
+ * Replace the :value placeholder in the given message.
+ *
+ * @param string $message
+ * @param string $value
+ * @return string
+ */
+ protected function replaceValuePlaceholder($message, $value)
+ {
+ return str_replace([':value'], [$value], $message);
+ }
+}
diff --git a/src/Rules/AsciiOnly.php b/src/Rules/AsciiOnly.php
new file mode 100644
index 0000000..16276ff
--- /dev/null
+++ b/src/Rules/AsciiOnly.php
@@ -0,0 +1,64 @@
+= (int) $parameters[0] && $size <= (int) $parameters[1];
+ }
+
+ /**
+ * Get the validation error message.
+ *
+ * @return string
+ */
+ public function message()
+ {
+ return "The :attribute value :value is not between :min - :max.";
+ }
+
+ /**
+ * Replace error messages parameter with right values
+ *
+ * @param string $message
+ * @param array $parameters
+ * @return string
+ */
+ public function parameterReplacer($message, $parameters)
+ {
+ return str_replace([':min', ':max'], [$parameters[0], $parameters[1]], $message);
+ }
+}
diff --git a/src/Validator/Rules.php b/src/Rules/Url.php
similarity index 77%
rename from src/Validator/Rules.php
rename to src/Rules/Url.php
index c0b5c86..63b1414 100644
--- a/src/Validator/Rules.php
+++ b/src/Rules/Url.php
@@ -1,40 +1,47 @@
= $min);
+ return true;
}
/**
- * @param $number
- * @param $max
- * @return bool
+ * Get the number of parameters that should be supplied
+ *
+ * @return integer
*/
- public function validateMax($number, $max)
+ public function parameterCount()
{
- return ($number <= $max);
+ return 0;
}
/**
- * @param $url
+ * Determine if the validation rule passes.
+ *
+ * @param mixed $value
+ * @param $parameters
* @return bool
*/
- public function validateUrl($url)
+ public function passes($value, $parameters)
{
/*
- * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (2.7.4).
- *
- * (c) Fabien Potencier http://symfony.com
- */
+ * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (2.7.4).
+ *
+ * (c) Fabien Potencier http://symfony.com
+ */
$pattern = '~^
((aaa|aaas|about|acap|acct|acr|adiumxtra|afp|afs|aim|apt|attachment|aw|barion|beshare|bitcoin|blob|bolo|callto|cap|chrome|chrome-extension|cid|coap|coaps|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-playcontainer|dlna-playsingle|dns|dntp|dtn|dvb|ed2k|example|facetime|fax|feed|feedready|file|filesystem|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|ham|hcp|http|https|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris.beep|iris.lwz|iris.xpc|iris.xpcs|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|ms-help|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|msnim|msrp|msrps|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|oid|opaquelocktoken|pack|palm|paparazzi|pkcs11|platform|pop|pres|prospero|proxy|psyc|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|secondlife|service|session|sftp|sgn|shttp|sieve|sip|sips|skype|smb|sms|smtp|snews|snmp|soap.beep|soap.beeps|soldat|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|things|thismessage|tip|tn3270|turn|turns|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|videotex|view-source|wais|webcal|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s)):// # protocol
(([\pL\pN-]+:)?([\pL\pN-]+)@)? # basic auth
@@ -51,16 +58,28 @@ public function validateUrl($url)
(/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment
$~ixu';
- return preg_match($pattern, $url) > 0;
+ return preg_match($pattern, $value) > 0;
}
/**
- * @param $string
- * @return bool
+ * Get the validation error message.
+ *
+ * @return string
*/
- public function validateAscii($string)
+ public function message()
{
- return (mb_detect_encoding($string, 'ASCII', true)) ? true : false;
+ return "The :attribute value :value is not a valid url";
}
+ /**
+ * Replace error messages parameter with right values
+ *
+ * @param string $message
+ * @param array $parameters
+ * @return string
+ */
+ public function parameterReplacer($message, $parameters)
+ {
+ return $message;
+ }
}
diff --git a/src/Validator/CsvValidator.php b/src/Validator/CsvValidator.php
deleted file mode 100644
index fe4ee27..0000000
--- a/src/Validator/CsvValidator.php
+++ /dev/null
@@ -1,307 +0,0 @@
-file_path = $file_path;
- }
- if (!empty($rules)) {
- $this->initialRules = $rules;
- }
- $this->delimiter = $delimiter;
- }
-
- /**
- * Returns method name for supplied rule
- * @param $rule
- * @return string
- */
- private function getRuleValidator($rule)
- {
- return "validate".ucfirst($rule);
- }
-
- /**
- * Validates that rules supplied are supported
- * Also validates that parameters are sent for rules that require parameters
- */
- private function validateInitialRules()
- {
- $invalidRulesError = [];
- if(!empty($this->initialRules)) {
- foreach ($this->initialRules as $key => $rules) {
- foreach ($rules as $rule => $parameter) {
- $validationFunction = $this->getRuleValidator($rule);
- // Checks if the rule supplied has a function for validating it
- if (!method_exists($this, $validationFunction) &&
- !is_callable(array($this, $validationFunction))
- ) {
- $invalidRulesError[$key][] = "Invalid rule {$rule} supplied.";
- } else {
- $func = new ReflectionMethod($this, $validationFunction);
- $numberOfParameters = $func->getNumberOfParameters();
-
- if ($numberOfParameters > 1 &&
- $parameter === "") {
- $invalidRulesError[$key][] = "Parameter for rule {$rule} cannot be empty.";
- }
- }
-
- }
- }
-
- if (!empty($invalidRulesError)) {
- throw new InvalidRuleDeclarationException($invalidRulesError);
- }
- }
- }
-
- /**
- * Read and validate CSV
- * @return $this
- */
- public function validate()
- {
- if ($this->validateFileExistAndReadable($this->file_path)) {
- $this->validateInitialRules();
- if (($handle = fopen($this->file_path, 'r')) !== FALSE) {
- while (($row = fgetcsv($handle, 0, ",")) !== FALSE) {
- $this->columnCount ++;
-
- if ($this->columnCount == 1) {
- $this->setHeaders($row);
- continue;
- }
-
- $result = [];
- //Loop through the CSV rows, validate against rules and assign correct values to object
- foreach ($row as $key => $value) {
-
- $attribute = $this->headers[$key];
- $validateAble = $this->isValidateAble($attribute);
-
- if ($validateAble) {
- $rule = $this->initialRules[$attribute];
- $validateAttr = $this->validateAttribute($value, $rule, $attribute);
- $result[$this->headers[$key]] = $value;
- if (!empty($validateAttr)) {
- $result["errors"] = $validateAttr;
- }
- } else {
- $result[$this->headers[$key]] = $value;
- }
- }
-
- if (!empty($result["errors"])) {
- $this->invalidData[] = $result;
- }else {
- $this->validData[] = $result;
- }
- }
- }
- }else {
- throw new InvalidArgumentException("The file path supplied is either not readable or doesn't exist.");
- }
-
- return $this;
-
- }
-
- /**
- * @return array
- */
- public function getHeaders()
- {
- return $this->headers;
- }
-
- /**
- * @param array $headers
- */
- private function setHeaders($headers)
- {
- foreach ($headers as $key => $value) {
- $this->headers[$key] = strtolower($value);
- }
- }
-
- /**
- * @return string
- */
- public function getFilePath()
- {
- return $this->file_path;
- }
-
- /**
- * @param string $file_path
- * @return $this
- */
- public function setFilePath($file_path)
- {
- $this->file_path = $file_path;
-
- return $this;
- }
-
- /**
- * @return array
- */
- public function getRules()
- {
- return $this->initialRules;
- }
-
- /**
- * @param array $initialRules
- * @return $this
- */
- public function setRules(array $initialRules)
- {
- $this->initialRules = $initialRules;
-
- return $this;
- }
-
- /**
- * @return array
- */
- public function getValidData()
- {
- return $this->validData;
- }
-
- /**
- * @return array
- */
- public function getInvalidData()
- {
- return $this->invalidData;
- }
-
- /**
- * @return string
- */
- public function getAllData()
- {
- return array_merge($this->validData, $this->invalidData);
- }
-
- /**
- * @param $attribute
- * @return bool
- */
- private function isValidateAble($attribute)
- {
- return (
- isset($this->initialRules[$attribute]) &&
- count(array_intersect($this->availableRules, array_keys($this->initialRules[$attribute]))) == count(array_keys($this->initialRules[$attribute]))
- );
- }
-
- /**
- * @param $file_path
- * @return bool
- */
- private function validateFileExistAndReadable($file_path)
- {
- return file_exists($file_path) && is_readable($file_path);
- }
-
- /**
- * @param $value
- * @param $rules
- * @return array
- */
- private function validateAttribute($value, $rules, $attribute)
- {
- $messages = [];
-
- foreach ($rules as $rule => $parameters) {
- $method = $this->getRuleValidator($rule);
- if(!$this->$method($value, $parameters)) {
- $messages[] = $this->getMessage($rule, $value, $attribute);
- }
- }
-
- return $messages;
-
- }
-
-}
\ No newline at end of file
diff --git a/src/Validator/ValidationRuleParser.php b/src/Validator/ValidationRuleParser.php
new file mode 100755
index 0000000..0066a43
--- /dev/null
+++ b/src/Validator/ValidationRuleParser.php
@@ -0,0 +1,71 @@
+filePath = $filePath;
+ $this->delimiter = $delimiter;
+ $this->rules = $rules;
+ $this->customMessages = $messages;
+
+ $this->setFileDirectory();
+ $this->setFileName();
+ }
+
+ /**
+ * Run the validator's rules against the supplied data.
+ */
+ public function validate()
+ {
+ if ($this->fails()) {
+ return $this->errors();
+ }
+
+ return [
+ "message" => self::SUCCESS_MESSAGE,
+ "data" => $this->data
+ ];
+ }
+
+ /**
+ * Return validation errors
+ */
+ public function errors()
+ {
+ if (empty($this->message) && empty($this->invalidRows)) {
+ $message = self::NO_ERROR_MESSAGE;
+ } elseif (empty($this->message)) {
+ $message = self::ERROR_MESSAGE;
+ } else {
+ $message = $this->message;
+ }
+
+ return [
+ "message" => $message,
+ "data" => $this->invalidRows
+ ];
+ }
+
+ /**
+ * Determine if the data fails the validation rules.
+ *
+ * @return bool
+ */
+ public function fails()
+ {
+ return ! $this->passes();
+ }
+
+ /**
+ * Determine if the data passes the validation rules.
+ *
+ * @return bool
+ */
+ protected function passes()
+ {
+ if ($this->doesFileExistAndReadable($this->filePath)) {
+ if (($handle = fopen($this->filePath, 'r')) !== FALSE) {
+ while (($row = fgetcsv($handle, 0, $this->delimiter)) !== FALSE) {
+ if (empty($this->headers)) {
+ $this->setHeaders($row);
+ continue;
+ }
+
+ $rowWithAttribute = [];
+
+ foreach ($row as $key => $value) {
+ $attribute = $this->headers[$key];
+ $rowWithAttribute[$attribute] = $value;
+ }
+
+ $this->validateRow($rowWithAttribute);
+ }
+ }
+ } else {
+ $this->message = self::INVALID_FILE_PATH_ERROR;
+ }
+
+ return empty($this->invalidRows) && empty($this->message);
+ }
+
+ /**
+ * Write the output data into any supplied format
+ *
+ * @param \Oshomo\CsvUtils\Contracts\ConverterHandlerInterface $format
+ * @return bool
+ */
+ public function write(Converter $format)
+ {
+ return $format
+ ->convert($this->data)
+ ->write($this->getWriteFileName($format->getExtension()));
+ }
+
+ /**
+ * Set CSV filename.
+ */
+ protected function setFileName()
+ {
+ $this->fileName = basename($this->filePath, self::FILE_EXTENSION);
+ }
+
+ /**
+ * Set CSV file directory.
+ */
+ protected function setFileDirectory()
+ {
+ $this->directory = dirname($this->filePath) . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ * Get the full path and name of the file to be written
+ *
+ * @param $extension
+ * @return string
+ */
+ protected function getWriteFileName($extension)
+ {
+ return $this->directory . $this->fileName . "." . $extension;
+ }
+
+ /**
+ * Validate a given row with the supplied rules.
+ *
+ * @param $row
+ */
+ protected function validateRow($row)
+ {
+ $this->currentRowMessages = [];
+ $this->currentRow = $row;
+
+ foreach ($this->rules as $attribute => $rules) {
+ foreach ($rules as $rule) {
+ $this->validateAttribute($attribute, $rule);
+ }
+ }
+
+ if (!empty($this->currentRowMessages)) {
+ $row['errors'] = $this->currentRowMessages;
+ $this->invalidRows[] = $row;
+ }
+
+ $this->data[] = $row;
+ }
+
+ /**
+ * Validate a given attribute against a rule.
+ *
+ * @param string $attribute
+ * @param string $rule
+ * @return null|void
+ */
+ protected function validateAttribute($attribute, $rule)
+ {
+
+ list($rule, $parameters) = ValidationRuleParser::parse($rule);
+
+ if ($rule == '') {
+ return;
+ }
+
+ $value = $this->getValue($attribute);
+
+ if ($rule instanceof ValidationRule) {
+ $this->validateUsingCustomRule($attribute, $value, $parameters, $rule);
+
+ return;
+ }
+
+ if ($this->isValidateAble($rule, $parameters)) {
+ $ruleClass = $this->getRuleClass($rule);
+ if (!$ruleClass->passes($value, $parameters)) {
+ $this->addFailure(
+ $this->getMessage($attribute, $ruleClass, $rule),
+ $attribute,
+ $value,
+ $ruleClass,
+ $parameters
+ );
+ }
+ }
+ }
+
+ /**
+ * @param $filePath
+ * @return bool
+ */
+ protected function doesFileExistAndReadable($filePath)
+ {
+ return file_exists($filePath) && is_readable($filePath);
+ }
+
+ /**
+ * @param array $headers
+ */
+ protected function setHeaders($headers)
+ {
+ $this->headers = $headers;
+ }
+
+ /**
+ * Determine if the attribute is validate-able.
+ *
+ * @param object|string $rule
+ * @param string $parameters
+ * @return bool
+ */
+ protected function isValidateAble($rule, $parameters)
+ {
+ return $this->ruleExists($rule) &&
+ $this->passesParameterCheck($rule, $parameters);
+ }
+
+ /**
+ * Get the class of a rule.
+ *
+ * @param $rule
+ * @return string
+ */
+ protected function getRuleClassName($rule)
+ {
+ return "Oshomo\\CsvUtils\\Rules\\".$rule;
+ }
+
+ /**
+ * Get the class of a rule.
+ *
+ * @param $rule
+ * @return \Oshomo\CsvUtils\Contracts\ValidationRuleInterface
+ */
+ protected function getRuleClass($rule)
+ {
+ $ruleClassName = $this->getRuleClassName($rule);
+
+ return new $ruleClassName();
+ }
+
+ /**
+ * Determine if a given rule exists.
+ *
+ * @param object|string $rule
+ * @return bool
+ */
+ protected function ruleExists($rule)
+ {
+ return $rule instanceof ValidationRule ||
+ class_exists($this->getRuleClassName($rule));
+ }
+
+ /**
+ * Determine if a given rule expect parameters and that the parameters where sent.
+ *
+ * @param object|string $rule
+ * @param $parameters
+ * @return bool
+ */
+ protected function passesParameterCheck($rule, $parameters)
+ {
+ if (!$rule instanceof ValidationRule) {
+ $rule = $this->getRuleClass($rule);
+ }
+
+ if ($rule->isImplicit()) {
+ return true;
+ } else {
+ $ruleParameterCount = $rule->parameterCount();
+ $parameterCount = count($parameters);
+ return ($ruleParameterCount === -1) ? ($parameterCount > 0) : ($parameterCount === $ruleParameterCount);
+ }
+
+ }
+
+ /**
+ * Validate an attribute using a custom rule object.
+ *
+ * @param string $attribute
+ * @param mixed $value
+ * @param $parameters
+ * @param \Oshomo\CsvUtils\Contracts\ValidationRuleInterface $rule
+ * @return void
+ */
+ protected function validateUsingCustomRule($attribute, $value, $parameters, $rule)
+ {
+ if (!$rule->passes($value, $parameters)) {
+ $this->addFailure($rule->message(), $attribute, $value, $rule, $parameters);
+ }
+ }
+
+ /**
+ * Add a failed rule and error message to the collection.
+ *
+ * @param $message
+ * @param string $attribute
+ * @param mixed $value
+ * @param \Oshomo\CsvUtils\Contracts\ValidationRuleInterface $rule
+ * @param array $parameters
+ * @return void
+ */
+ protected function addFailure($message, $attribute, $value, $rule, $parameters = [])
+ {
+ $this->currentRowMessages[] = $this->makeReplacements(
+ $message, $attribute, $value, $rule, $parameters
+ );
+ }
+
+ /**
+ * Get the value of a given attribute.
+ *
+ * @param string $attribute
+ * @return mixed
+ */
+ protected function getValue($attribute)
+ {
+ return $this->currentRow[$attribute];
+ }
+}
diff --git a/src/Validator/ValidatorMessageTrait.php b/src/Validator/ValidatorMessageTrait.php
deleted file mode 100644
index 01077fd..0000000
--- a/src/Validator/ValidatorMessageTrait.php
+++ /dev/null
@@ -1,76 +0,0 @@
-$messageRuleMethodName($value, $attribute);
- }
-
- return $message;
- }
-
-}
\ No newline at end of file
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
old mode 100644
new mode 100755
diff --git a/tests/data/ascii_test.csv b/tests/data/ascii_test.csv
new file mode 100644
index 0000000..c7d467b
--- /dev/null
+++ b/tests/data/ascii_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/files/test.csv b/tests/data/between_test.csv
similarity index 55%
rename from tests/files/test.csv
rename to tests/data/between_test.csv
index f275624..d868f8a 100644
--- a/tests/files/test.csv
+++ b/tests/data/between_test.csv
@@ -1,3 +1,2 @@
name,address,stars,contact,uri
-Well Health Hotels,Inga N. P.O. Box 567,8,Kasper Zen,http://example.com
-Well Health Hotels,Inga N. P.O. Box 567,3,Kasper Zen,http//:well.org
\ No newline at end of file
+Well Health Hotels,Inga N. P.O. Box 567,3,Kasper Zen,http//:well.org
diff --git a/tests/data/url_test.csv b/tests/data/url_test.csv
new file mode 100644
index 0000000..d868f8a
--- /dev/null
+++ b/tests/data/url_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/valid_test.csv b/tests/data/valid_test.csv
new file mode 100644
index 0000000..1ec60df
--- /dev/null
+++ b/tests/data/valid_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/valid_test_expected.json b/tests/data/valid_test_expected.json
new file mode 100644
index 0000000..2993e77
--- /dev/null
+++ b/tests/data/valid_test_expected.json
@@ -0,0 +1,9 @@
+[
+ {
+ "name": "Well Health Hotels",
+ "address": "Inga N. P.O. Box 567",
+ "stars": 3,
+ "contact": "Kasper Zen",
+ "uri": "http://well.org"
+ }
+]
\ No newline at end of file
diff --git a/tests/data/valid_test_expected.xml b/tests/data/valid_test_expected.xml
new file mode 100644
index 0000000..ca98844
--- /dev/null
+++ b/tests/data/valid_test_expected.xml
@@ -0,0 +1,10 @@
+
+
+ -
+ Well Health Hotels
+ Inga N. P.O. Box 567
+ 3
+ Kasper Zen
+ http://well.org
+
+
diff --git a/tests/data/valid_test_param_expected.xml b/tests/data/valid_test_param_expected.xml
new file mode 100644
index 0000000..a8d64e4
--- /dev/null
+++ b/tests/data/valid_test_param_expected.xml
@@ -0,0 +1,10 @@
+
+
+
+ Well Health Hotels
+ Inga N. P.O. Box 567
+ 3
+ Kasper Zen
+ http://well.org
+
+
diff --git a/tests/files/test-expected.json b/tests/files/test-expected.json
deleted file mode 100644
index 2dd2880..0000000
--- a/tests/files/test-expected.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[
- {
- "name": "Well Health Hotels",
- "address": "Inga N. P.O. Box 567",
- "stars": 8,
- "contact": "Kasper Zen",
- "uri": "http://example.com"
- },
- {
- "name": "Well Health Hotels",
- "address": "Inga N. P.O. Box 567",
- "stars": 3,
- "contact": "Kasper Zen",
- "uri": "http//:well.org"
- }
-]
\ No newline at end of file
diff --git a/tests/files/test-expected.xml b/tests/files/test-expected.xml
deleted file mode 100644
index 661e630..0000000
--- a/tests/files/test-expected.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- Well Health Hotels
- Inga N. P.O. Box 567
- 8
- Kasper Zen
- http://example.com
-
-
- Well Health Hotels
- Inga N. P.O. Box 567
- 3
- Kasper Zen
- http//:well.org
-
-
diff --git a/tests/src/Converter/CsvConverterTest.php b/tests/src/Converter/CsvConverterTest.php
deleted file mode 100644
index 13bfa2d..0000000
--- a/tests/src/Converter/CsvConverterTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-test_assets = realpath(dirname(__FILE__) . "/../../files");
- $validator = new CsvValidator($this->test_assets . "/test.csv");
- $this->converter = new CsvConverter([], $this->test_assets);
- $this->object = $validator->validate()->getAllData();
- }
-
- public function testConstructorParams()
- {
- $this->assertEquals($this->test_assets, $this->converter->getPath());
- $this->assertEmpty($this->converter->getData());
- }
-
- public function testInvalidDirectoryPathException()
- {
- $this->expectException(InvalidArgumentException::class);
- $this->converter->setPath($this->test_assets . "/test.csv");
- }
-
- public function testSetData()
- {
- $this->converter->setData($this->object);
- $this->assertNotEmpty($this->converter->getData());
- }
-
- public function testToJson()
- {
- $this->converter->setData($this->object);
- $this->converter->toJson("test.json");
- $this->assertFileExists($this->test_assets . "/test.json");
- $this->assertFileEquals($this->test_assets . "/test-expected.json", $this->test_assets . "/test.json");
- }
-
- public function testToXml()
- {
- $this->converter->setData($this->object);
- $this->converter->toXml("test.xml");
- $this->assertFileExists($this->test_assets . "/test.xml");
- $this->assertFileEquals($this->test_assets . "/test-expected.xml", $this->test_assets . "/test.xml");
- }
-
-}
\ No newline at end of file
diff --git a/tests/src/Converter/RuleTest.php b/tests/src/Converter/RuleTest.php
deleted file mode 100644
index 673277f..0000000
--- a/tests/src/Converter/RuleTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-object = new Rules();
- }
-
- public function testValidateMin()
- {
- $this->assertTrue($this->object->validateMin(5, 3));
- $this->assertTrue($this->object->validateMin(5, 5));
- $this->assertFalse($this->object->validateMin(3, 5));
- }
-
- public function testValidateMax()
- {
- $this->assertTrue($this->object->validateMax(3, 5));
- $this->assertTrue($this->object->validateMax(3, 3));
- $this->assertFalse($this->object->validateMax(5, 3));
- }
-
- public function testValidateUrl()
- {
- // Characters that should have been escaped?
- $this->assertFalse($this->object->validateUrl("http://stackoverflow.com/users/9999999/not a-real-user"));
- // Accidentally transposed characters?
- $this->assertFalse($this->object->validateUrl("http//:stackoverflow.com/questions/9715606/bad-url-test-cases"));
- // URL with no protocol
- $this->assertFalse($this->object->validateUrl("example.com"));
- // Correct URL with protocol and domain name
- $this->assertTrue($this->object->validateUrl("http://example.com"));
- }
-
-}
\ No newline at end of file
diff --git a/tests/src/CsvValidatorParserTest.php b/tests/src/CsvValidatorParserTest.php
new file mode 100755
index 0000000..9695fe7
--- /dev/null
+++ b/tests/src/CsvValidatorParserTest.php
@@ -0,0 +1,36 @@
+assertSame(
+ [$customRule, []],
+ ValidationRuleParser::parse($customRule)
+ );
+ }
+
+ public function testWhenOtherRulesArePassed()
+ {
+ $this->assertSame(
+ ["AsciiOnly", []],
+ ValidationRuleParser::parse("ascii_only")
+ );
+ }
+
+ public function testWhenRulesAcceptParameters()
+ {
+ $this->assertSame(
+ ["Between", ["1", "3"]],
+ ValidationRuleParser::parse("between:1,3")
+ );
+ }
+
+}
diff --git a/tests/src/CsvValidatorTest.php b/tests/src/CsvValidatorTest.php
new file mode 100755
index 0000000..6ee28ea
--- /dev/null
+++ b/tests/src/CsvValidatorTest.php
@@ -0,0 +1,275 @@
+testAssets = realpath(dirname(__FILE__) . "/../data");
+ }
+
+ public function testInvalidCsvFilePath()
+ {
+ $file = $this->testAssets . "/tests.csv";
+
+ $validator = new Validator($file, ',', [
+ "stars" => ["between:0,5"]
+ ]);
+
+ $this->assertSame(
+ $validator::INVALID_FILE_PATH_ERROR,
+ $validator->validate()['message']
+ );
+ }
+
+ public function testAsciiOnlyValidationRule()
+ {
+ $file = $this->testAssets . "/ascii_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "name" => ["ascii_only"]
+ ]);
+
+ $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¡ contains a non-ascii character",
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testBetweenValidationRule()
+ {
+ $file = $this->testAssets . "/between_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "stars" => ["between:4,10"]
+ ]);
+
+ $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 is not between 4 - 10.",
+ $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']
+ );
+
+ $this->assertArrayHasKey(
+ "errors",
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ "The uri value http//:well.org is not a valid url",
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testValidatorWithCustomRule()
+ {
+ $file = $this->testAssets . "/ascii_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "name" => [new UppercaseRule]
+ ]);
+
+ $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¡ must be uppercase.",
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testValidatorWithCustomErrorMessage()
+ {
+ $file = $this->testAssets . "/ascii_test.csv";
+ $customErrorMessage = "The value supplied for the name attribute must only contain ascii characters";
+
+ $validator = new Validator($file, ',', [
+ "name" => ["ascii_only"]
+ ], [
+ "ascii_only" => $customErrorMessage
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ "errors",
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ $customErrorMessage,
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testValidatorWithCustomErrorMessageWithPlaceholder()
+ {
+ $file = $this->testAssets . "/between_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "stars" => ["between:4,10"]
+ ], [
+ "between" => "The value supplied for :attribute must be between :min and :max"
+ ]);
+
+ $this->assertTrue($validator->fails());
+
+ $this->assertSame(
+ $validator::ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertArrayHasKey(
+ "errors",
+ $validator->errors()['data'][0]
+ );
+
+ $this->assertContains(
+ "The value supplied for stars must be between 4 and 10",
+ $validator->errors()['data'][0]['errors']
+ );
+ }
+
+ public function testValidatorJsonWriter()
+ {
+ $file = $this->testAssets . "/valid_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "name" => ["ascii_only"],
+ "stars" => ["between:3,10"],
+ "uri" => ["url"]
+ ]);
+
+ $this->assertFalse($validator->fails());
+
+ $this->assertSame(
+ $validator::NO_ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertTrue($validator->write(new JsonConverter()));
+
+ $this->assertFileEquals(
+ $this->testAssets . "/valid_test_expected.json",
+ $this->testAssets . "/valid_test.json"
+ );
+ }
+
+ public function testValidatorXmlWriter()
+ {
+ $file = $this->testAssets . "/valid_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "name" => ["ascii_only"],
+ "stars" => ["between:3,10"],
+ "uri" => ["url"]
+ ]);
+
+ $this->assertFalse($validator->fails());
+
+ $this->assertSame(
+ $validator::NO_ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertTrue($validator->write(new XmlConverter()));
+
+ $this->assertFileEquals(
+ $this->testAssets . "/valid_test_expected.xml",
+ $this->testAssets . "/valid_test.xml"
+ );
+ }
+
+ public function testValidatorXmlWriterWithRecordElementParameter()
+ {
+ $file = $this->testAssets . "/valid_test.csv";
+
+ $validator = new Validator($file, ',', [
+ "name" => ["ascii_only"],
+ "stars" => ["between:3,10"],
+ "uri" => ["url"]
+ ]);
+
+ $this->assertFalse($validator->fails());
+
+ $this->assertSame(
+ $validator::NO_ERROR_MESSAGE,
+ $validator->errors()['message']
+ );
+
+ $this->assertTrue($validator->write(new XmlConverter('sample')));
+
+ $this->assertFileEquals(
+ $this->testAssets . "/valid_test_param_expected.xml",
+ $this->testAssets . "/valid_test.xml"
+ );
+ }
+
+}
diff --git a/tests/src/UppercaseRule.php b/tests/src/UppercaseRule.php
new file mode 100644
index 0000000..e28b89f
--- /dev/null
+++ b/tests/src/UppercaseRule.php
@@ -0,0 +1,53 @@
+test_assets = realpath(dirname(__FILE__) . "/../../files");
- $this->validator = new CsvValidator();
- $this->validator->setFilePath($this->test_assets . "/test.csv");
- }
-
- public function testConstructorParams()
- {
- $file = $this->test_assets . "/test.csv";
- $rule = [
- "name" => ["ascii" => ""]
- ];
- $validator = new CsvValidator($file, $rule);
- $this->assertEquals($file, $validator->getFilePath());
- $this->assertNotEmpty($validator->getRules());
- }
-
- public function testWhenInvalidRuleIsPresent()
- {
- $invalidRule = [
- "name" => ["notset" => ""]
- ];
- $this->expectException(InvalidRuleDeclarationException::class);
- $this->validator->setRules($invalidRule)->validate();
- }
-
- public function testWhenNoParameterIsSuppliedForRule()
- {
- $invalidRule = [
- "name" => ["min" => ""]
- ];
- $this->expectException(InvalidRuleDeclarationException::class);
- $this->validator->setRules($invalidRule)->validate();
- }
-
- public function testWrongFilePathException()
- {
- $this->expectException(InvalidArgumentException::class);
- $this->validator->setFilePath($this->test_assets . "/tet.csv")->validate();
- }
-
- public function testValidatorMessageTraitMessages()
- {
- $this->assertEquals("ratings attribute value 3 is less than the specified minimum.", $this->validator->getMessage("min", 3, "ratings"));
- $this->assertEquals("stars attribute value 5 is greater than the specified maximum.", $this->validator->getMessage("max", 5, "stars"));
- $this->assertEquals("website attribute value http//:test.com is not a valid url.", $this->validator->getMessage("url", "http//:test.com", "website"));
- $this->assertEquals("name attribute value test ascii string contains one or more ascii character.", $this->validator->getMessage("ascii", "test ascii string", "name"));
- }
-
- public function testGetHeadersBeforeValidate()
- {
- $this->validator->setRules([]);
-
- $this->assertEmpty($this->validator->getHeaders());
- }
-
- public function testGetHeadersAfterValidate()
- {
- $this->validator->setRules([])->validate();
-
- $this->assertEquals(['name', 'address', 'stars', 'contact', 'uri'], $this->validator->getHeaders());
- }
-
- public function testValidate()
- {
- $this->validator->setRules(
- [
- "stars" => ["min" => 4],
- "uri" => ["url" => ""]
- ]
- )->validate();
-
- $this->assertEquals(2, count($this->validator->getAllData()));
- $this->assertEquals(1, count($this->validator->getInvalidData()));
- $this->assertEquals(1, count($this->validator->getValidData()));
- $this->assertEquals(1, count($this->validator->getInValidData()[0]['errors']));
- $this->assertEquals("uri attribute value http//:well.org is not a valid url.", $this->validator->getInValidData()[0]['errors'][0]);
- }
-
-}
\ No newline at end of file