Laravel-Excel: Only one failure returned when using WithValidation concern

Prerequisites

  • Able to reproduce the behaviour outside of your code, the problem is isolated to Laravel Excel.
  • Checked that your issue isn’t already filed.
  • Checked if no PR was submitted that fixes this problem.

Versions

  • PHP version: 7.1.18
  • Laravel version: 5.7.13
  • Package version: 3.1.3

Description

I’m working on an Import class that uses concerns WithHeadingRow and WithValidation. I’ve copied the try/catch in the docs for gathering failures, but it is only catching one failure at a time (an array with one entry), rather than gathering all validation errors for all rows into an array, as the documentation says. The import is otherwise working as expected, and it is validating correctly, but apparently stopping after the first validation failure.

Steps to Reproduce

Relevant controller code:

        try {
            Excel::import(new MaterialHazardImport(), $path);
        } catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
            $failures = $e->failures();
            return view('admin.material-hazard-import.errors', compact('failures'));
        } 

Relevant import class code:

<?php

namespace App\Imports;

use App\Models\MaterialHazardImportTask;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;

class MaterialHazardImport implements ToModel, WithHeadingRow, WithValidation
{
    public function model(array $row)
    {
        if (isset($row['casrn_or_material_id'])) {
            return new MaterialHazardImportTask([
                'hazard_id' => $row['hazard_id'],
                'casrn' => $row['casrn_or_material_id'],
                'name' => $row['chemical_or_material_name'],
            ]);
        }
    }

    public function rules(): array
    {
        return [
            'hazard_id' => 'required',
            'casrn_or_material_id' => 'required',
            'chemical_or_material_name' => 'required',
        ];
    }
}

Expected behavior:

Uploading a template with multiple rows that have “chemical_or_material_name” missing should yield a $failures array with multiple entries.

Actual behavior:

Only one failure is returned in the array, as shown in my errors view. Example below: screen shot 2018-12-18 at 3 50 16 pm

Additional Information

Any additional information, configuration or data that might be necessary to reproduce the issue.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 19 (9 by maintainers)

Most upvoted comments

If you want to catch all validation errors, you need to use batch inserts. Without it, it will validate row per row.

Without implementing skipsOnFailure

  • Without batchInsert a validation exception will be thrown at first chance, and rollback. Only access to that first validation error
  • With batchInsert its the same, but there’ll be access to all the validation errors included in that batch

Implementing skipsOnFailure

  • You will have access to every validation error via the onFailure method $failures parameter
  • No excepction by default will be thrown, so you should make sure to return null from the model method if the validation isn’t met. (But that would mean checking the same rule twice, right?)
  • Nothing would be rolledback by default

Batch inserts are meant to avoid repeating an unnecessary amount of queries. At the same time, in case of any failure the batch currently running will be rolledback along with the previous ones, and without having to read the subsequent ones.

So the only way to insert all the valid rows and keep track of all the failures, is loading the whole file, implementing skipsOnFailure and saving each failure to retrieve them later via a getter. In this case batchInserts would just do the favor of avoiding possible bottleneck effect.

Please clarify or confirm if i’m getting it right. PD: I know about the trait that is now available.

@Nutbolt52 Thanks for your PR by the way 😉

Thank you @patrickbrouwers! That seems to have solved it, and when you put it like that, it kind of makes sense why it works that way now. Maybe just a note in the documentation for the idiots out there like me 😃.

So just to make sure I am 100% understanding, if I have batch size set to 100, it will only show validation issues on the first 100 rows, I won’t see any validation issues on the rows 100+ in the next batch?