This is a really simple example of using Laravel’s built-in Validation to validate the contents of a CSV File Upload. Having searched around I found the following package Konafets’ CsvValidator However at the time of writing, this does not work or install correctly with Composer.
Taking Konafet’s as a base I created the following Rule class. It will parse in a CSV file then check each column against a set of rules. The class looks as follows
<?php namespace App\Rules; use Illuminate\Support\Facades\Validator; class CsvValidator { private $csvData; private $rules; private $headingRow; private $errors; private $headingKeys = []; public function construct() {} /** * @param $csvPath * @param $rules * @param string $encoding * @return $this * @throws \Exception */ public function open($csvPath, $rules, $encoding = 'UTF-8') { $this->csvData = []; $this->setRules($rules); $csvData = $this->getCsvAsArray($csvPath); if (empty($csvData)) { throw new \Exception('No data found.'); } $newCsvData = []; $ruleKeys = array_keys($this->rules); foreach ($csvData as $rowIndex => $csvValues) { foreach ($ruleKeys as $ruleKeyIndex) { $newCsvData[$rowIndex][$ruleKeyIndex] = $csvValues[$ruleKeyIndex]; } } $this->csvData = $newCsvData; return $this; } /** * Given a File Path, convert to associate array. * If keyField is set then this will be used as a key else * it will use normal ascending indexes. * * E.g. when keyField = 'ID' * data in: * ID | Name * 10,Rick * data out: * [10 => ['ID' => 10, 'Name' => 'Rick']] * * E.g. when keyField = null * data in: * ID | Name * 10,Rick * data out: * [0 => ['ID' => 10, 'Name' => 'Rick']] * * @param string $filePath * @param string|null $keyField * @return array */ public function getCsvAsArray($filePath, $keyField = null) { $rows = array_map('str_getcsv', file($filePath)); $rowKeys = array_shift($rows); $formattedData = []; foreach ($rows as $row) { $associatedRowData = array_combine($rowKeys, $row); if (empty($keyField)) { $formattedData[] = $associatedRowData; } else { $formattedData[$associatedRowData[$keyField]] = $associatedRowData; } } return $formattedData; } public function fails() { $errors = []; foreach ($this->csvData as $rowIndex => $csvValues) { $validator = Validator::make($csvValues, $this->rules); if (!empty($this->headingRow)) { $validator->setAttributeNames($this->headingRow); } if ($validator->fails()) { $errors[$rowIndex] = $validator->messages()->toArray(); } } $this->errors = $errors; return (!empty($this->errors)); } public function getErrors() { return $this->errors; } public function getData() { return $this->csvData; } public function setAttributeNames($attribute_names) { $this->headingRow = $attribute_names; } private function setRules($rules) { $this->rules = $rules; $this->headingKeys = array_keys($rules); } }
This can be use in a controller action as follows:
$rules = [ 'Some string' => 'required|string', 'User Id' => 'required|exists:users,id', ]; try { $filePath = ..getfilepath $csvValidator = (new CsvValidator)->open($filePath, $rules); if ($csvValidator->fails()) { $errors = $csvValidator->getErrors(); // ... return and error etc. } } catch (\Exception $e) { // ... return and error etc. } // Success - Get data $csvData = $csvValidator->getData(); // .... Use data as you want
The above can be extended to handle different file encodings etc. but was fine for a simple CSV validator.
Comments are closed.