I will be writing a small command line script to read and validate a CSV file. For the validation part I will be using the Symfony Validator Component,
Source of inspiration: https://blog.tinned-software.net/using-the-symfony-validator-as-a-standalone-component/
First step, install Symfony Validator using Composer:
composer require symfony/validator
and require the autoloader created by Composer.
<?php require_once 'vendor/autoload.php'; require_once 'Loader.php'; use Symfony\Component\Validator\Validation; $file = "someFile.csv"; $outputFile = "errorsLog" . "_" . time() . ".txt"; $validator = Validation::createValidatorBuilder() ->addMethodMapping('loadValidatorMetadata') ->getValidator() ; $loader = new Loader($file, $outputFile, $validator); $loader->load();
I will pass to my Loader class constructor the CSV file to be read, the file where I want to save the output and an instance of the Symfony validator.
The CSV file is read line by line, and from each line I will be creating a Row object. The validator will validate the Row object against the rules (constraints).
In the Row class I will add the method "loadValidatorMetada" mentioned when instantiating the Validator. I've added several validations for better examplification:
<?php use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Mapping\ClassMetadata; class Row { private $id; private $name; private $personalNumber; private $CountryCode; public function __construct($rowNumber, $data) { $this->id = $rowNumber; $this->name= $data[0]; $this->personalNumber = $data[2]; $this->countryCode = $data[1]; } /** * This method is where you define your validation rules. */ public static function loadValidatorMetadata(ClassMetadata $metadata) { //Name
$metadata->addPropertyConstraint('name', new Assert\NotBlank());
//Personal number $metadata->addPropertyConstraint('personalNumber', new Assert\NotBlank());
$metadata->addPropertyConstraint('personalNumber', new Assert\Type(array( 'type' => 'digit', 'message' => 'The value {{ value }} is not a valid personal number.', ))); //Country code $metadata->addPropertyConstraint('countryCode', new Assert\NotBlank()); $metadata->addPropertyConstraint('countryCode', new Assert\Choice(array( 'choices' => array('DE', 'AT', 'LU', 'ES', 'FR', 'BE', 'NO', 'SI', 'SE', 'IT', 'DK'), 'message' => '{{ value }} is not a valid country code!', ))); } }
When validating a Row object against these constraints an array of Errors will be returned by the Validator. Below is the Loader class.
<?php require_once 'Row.php'; class Loader { private $fileName; private $validator; private $outputFile; public function __construct($fileName, $outputFile, $validator) { $this->fileName = $fileName; $this->outputFile = $outputFile; $this->validator = $validator; } public function load() { $file_handle = fopen($this->fileName, "r"); $fileOutputHandle = fopen($this->outputFile, "w"); /* * Keep track of the current row in .csv file */ $rowNumber=1; /* * Read the file line by line */ while (!feof($file_handle) ) { $line_of_text = fgetcsv($file_handle, 0); if ($rowNumber === 1) { /* * Ignore the first row from file as it contains headers */ } else { $row = new Row($rowNumber, $line_of_text); $this->validateRow($row, $fileOutputHandle); } $rowNumber++; } fclose($file_handle); fclose($fileOutputHandle); } public function validateRow(Row $row, $fileOutputHandle) { $errors = $this->validator->validate($row); foreach ($errors as $error) { $errorMsg = "At row:" . $error->getRoot()->getId() . "- Property: " . $error->getPropertyPath() . ' - Message: ' . $error->getMessage() . "\n"; fwrite($fileOutputHandle, $errorMsg); } } }