noblemfd's avatar

How to save all import errors into a table

In my Laravel-8, I am uploading from Excel into the DB using Maatwebsite package:

Import:

In my Laravel-8 and Maatwebsite-3.1 package, I have this code:

use App\Models\User;
use App\Models\Country;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\SkipsErrors;
use Maatwebsite\Excel\Concerns\SkipsOnError;
use Maatwebsite\Excel\Concerns\SkipsFailures;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Validators\Failure;
use Throwable;

class CountryImport implements
    ToModel,
    WithValidation,
    WithHeadingRow,
    SkipsOnError,
    SkipsOnFailure,
    WithBatchInserts
{
    use Importable, SkipsErrors, SkipsFailures;

    public function model(array $row)
    {
        return new Country([
            'name'                              => $row['name],
            'nationality'                       => $row['nationality],
            'created_at'                        => date("Y-m-d H:i:s"),
            'created_by'                        => Auth::user()->id,
        ]);
    }

    public function headingRow(): int
    {
        return 1;
    }

    public function rules(): array
    {
        return [
            '*.name' => [
                'required',
                'string',
                'max:100',
                Rule::unique('countries', 'name')
            ],
            '*.nationality' => [
                'nullable',
                'string',
                'max:50',
                Rule::unique('countries', 'nationality')
            ],
        ];
    }

    public function batchSize(): int
    {
        return 1000;
    }

    public function chunkSize(): int
    {
        return 1000;
    }
}

Controller:

public function importCountry(Request $request)
{
    $user = Auth::user()->id;
    $userEmail = Auth::user()->email;
            $file = $request->file('document')->store('import');
            $import = new CountryImport;
            $import->import($file);

            if($import->failures()->isNotEmpty())
            {
                return $this->error($import->failures(), 422);
            }
            dd($import->failures());

            return $this->success('Nationalities Successfully Imported.', [
                'file'         => $file
            ]);
}

I made the upload to skip failures and errors during upload.

return $this->error($import->failures(), 422);

gives:

{
 "message": [
    {
        "row": 2,
        "attribute": "Country",
        "errors": [
            "The Country has already been taken."
        ],
        "values": {
            "country_name_full": "Afghanistan",
            "nationality": "Afghan",
        }
    },
    {
        "row": 3,
        "attribute": "Country",
        "errors": [
            "The Country has already been taken."
        ],
        "values": {
            "country_name_full": "Albania",
            "nationality": "Albanian",
        }
    }
 ],
 "error": true,
 "code": 422
}

while dd($import->failures());

gives:

Illuminate\Support\Collection {#1542
 #items: array:184 [
  0 => Maatwebsite\Excel\Validators\Failure {#2917605
   #row: 2
   #attribute: "Country"
   #errors: array:1 [
     0 => "The Country has already been taken."
   ]
   -values: array:7 [
    "name" => "Afghanistan"
    "nationality" => "Afghan"
    "" => null
  ]
}
1 => Maatwebsite\Excel\Validators\Failure {#2933982
  #row: 3
  #attribute: "Country"
  #errors: array:1 [
    0 => "The Country has already been taken."
  ]
  -values: array:7 [
    "name" => "Albania"
    "nationality" => "Albanian"
    "" => null
  ]
}

I have a Model called ImportError with these fields

ImportError (row, attribute, error, values)

Since I allow SkipOnFailure and SkipOnError. In the same controller, how do I automatically store these errors in the ImportError Models immediately After Import so that users can access it later?

0 likes
1 reply

Please or to participate in this conversation.