Validate CSV row content with Custom validation rules with FormRequest in Laravel
Tags: laravel, formrequest, validationrule, csv
Published: 2022-03-14 10:47:35

In this example I am going to show and brief my implementation of validating CSV row content.

## Scenario

I need to create a simple custom CSV importer to import a few rows into my Laravel application database. I want to validate the incoming data in the columns of each row to make sure that data is what we expect to be. For example, some of columns should be string type and some integer etc. 

## Solution

Since I am using FormRequest validation method I need to implement it to adapt to FormRequest mechanism. I thank to [this post](https://egndigital.com/laravel-validate-csv-content/) for providing me a base for it.

I am not going to create a new Laravel application and I will just show the relevant code only.

Here is the `store` method of `UserController.php`

```php

namespace App\Http\Controllers\API;

use App\Http\Requests\UserImportRequest;
use App\Http\Controllers\Controller;
use App\Http\Traits\CsvParser;
use App\Models\User;

class UserController extends Controller
{
    use CsvParser;

public function store( UserImportRequest $request, Merchant $merchant )
{
    $fileContents = request()->file('field_csvfile')->get();
    $csvData = $this->CsvToArray($fileContents);
    $imported = [];
    foreach( $csvData as $row ) {
        $imported[] = User::create(
            $row
        );
    }
    return response( $imported );
}
```
Here is `UserImportRequest.php`, the FormRequest file

```php
namespace App\Http\Requests;

use Illuminate\Validation\Factory as ValidationFactory;
use \Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
use Illuminate\Foundation\Http\FormRequest;
use App\Rules\CsvContent;

class UserImportRequest extends FormRequest
{
    public function __construct(ValidationFactory $validationFactory)
    {
        $validationFactory->extend(
            'dob_custom_check',
            function ($attribute, $value, $parameters) {
                //If valid date ; write your own "date check" logic
                return true; //Else
                //return false
            },
            ':attribute is not valid'
        );
    }
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'field_csvfile' => [
                'bail',
                'file',
                'mimes:csv,txt,xls,xlsx',
                new CsvContent( [
                    'first_name' => 'required|date',
                    'last_name' => 'required|date',
                    'email' => ['required','unique:users'],
                    'date_of_birth' => ['required, 'date', 'dob_custom_check'],
                    'address' => 'required|string',
                    '' => 'required|string',
                ])
            ]
        ];
    }
}
```
Here is the `CsvParser` trait `App\Http\Traits\CsvParser.php`

```php
namespace App\Http\Traits;

trait CsvParser {
    public function CsvToArray( $content )    {
        $rows = array_map('str_getcsv', explode(PHP_EOL, $content));
        $rowKeys = array_shift($rows);
        $formattedData = [];
        foreach ($rows as $row) {
            if( sizeof($row) == sizeof($rowKeys) )  {
                $associatedRowData = array_combine($rowKeys, $row);
                if (empty($keyField)) {
                    $formattedData[] = $associatedRowData;
                } else {
                    $formattedData[$associatedRowData[$keyField]] = $associatedRowData;
                }
            }
        }
        return $formattedData;
    }
}
```
I hope it helps someone.