I have tried to import csv using laravel excel and queue.I have tested import working without implement queue.its working perfectly.I was validate all rows before uploading csv.If validation error a mail will send to admin.Everything working fine without using queue.When i was using queue if no validation error it working.But if import function return validation error, error handling not working.it not hit the catch section.i checked the failed job.In exception column getting "maatwebsite\Excel\Validators\ValidationException: The given data was invalid. in C:\xampp\htdocs\user_import\vendor\maatwebsite\excel\src\Validators\RowValidator.php:68......".I dont know why error handling is not working in queue running.
controller
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\ImportUser;
use App\Http\Requests\UserImportRequest;
use App\Events\ImportFailEvent;
class UserImportController extends Controller
{
public function import(UserImportRequest $request)
{
try{
Excel::import(new ImportUser, $request->file('file'));
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
$failures = $e->failures();
$failer_array = [];
foreach ($failures as $failure) {
$failer_array[] = $failure->errors()[0].$failure->row();
}
//send mail
event(new ImportFailEvent($failer_array));
}
}
}
import user
namespace App\Imports;
use App\Models\ImportedUser;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\WithValidation;
class ImportUser implements ToCollection,WithValidation,WithHeadingRow,WithBatchInserts, WithChunkReading, ShouldQueue
{
public function rules(): array
{
return [
'user_code' => 'required|regex:/^[a-bA-B0-9 ]+$/',
'user_name' => 'required',
'user_address' => 'required',
];
}
public function customValidationMessages()
{
return [
'user_code.required' => 'User Code is missing at row ',
'user_code.regex' => 'User Code contains symbols at row ',
'user_name.required' => 'User Name is missing at row ',
'user_address.required' => 'User Address is missing at row ',
];
}
public function collection(Collection $rows)
{
foreach ($rows as $row)
{
ImportedUser::create([
'user_code' => $row['user_code'],
'user_name' => $row['user_name'],
'user_address' => $row['user_address'],
]);
}
}
public function batchSize(): int
{
return 500;
}
public function chunkSize(): int
{
return 500;
}
}
Related
public function onFailure(\Maatwebsite\Excel\Validators\Failure ...$failures)
{
$data = [];
foreach ($failures as $failure) {
$data[] = [
'row' => $failure->row(),
'attribute' => $failure->attribute(),
'values' => json_encode($failure->values()),
'errors' => json_encode($failure->errors()),
'module' => 'User',
];
}
dd($data);
Excel::download(new FailureExportPI($data),'failure_imports.xlsx');
}
i am trying to handle on failure items and download them in excel file but it is not working. when i die dump dd($data) i get the items but the issue is when i pass the data to download nothing happens my export is as below
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromView;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class FailureExportPI implements FromView,ShouldAutoSize
{ public function __construct($data)
{
$this->data=$data;
// dd($this->data);
}
public function view(): View
{
return view('mfiintpi.error',['data'=>$this->data]);
}
}
what am i doing wrong?
I'm following this example from the docs to handle multiple sheets import.
My Controller:
public function import(Request $request) {
$file = $request->file('import')->store('/storage');
$import = new MultisheetContactsImport();
$import->import($file);
if ($import->failures()->isNotEmpty()) {
return $import->failures();
}
return $import->getRowCount();
}
My import class ContactsImport.php
namespace App\Imports;
use App\Models\Email;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\withHeadingRow;
use Maatwebsite\Excel\Concerns\SkipsOnError;
use Maatwebsite\Excel\Concerns\SkipsErrors;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\SkipsFailures;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Illuminate\Support\Facades\Validator;
use Throwable;
class ContactsImport implements ToModel, withHeadingRow, SkipsOnError, WithValidation, SkipsOnFailure
{
private $rows = 0;
use Importable, SkipsErrors, SkipsFailures;
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
++$this->rows;
return new Email([
'apiKey' => Str::random(16),
'firstname' => $row['firstname'],
'lastname' => $row['lastname'],
'emailAddress' => $row['emailaddress'],
'businessType' => $row['businesstype'],
'allowed' => true,
'updates' => true,
'marketing' => true,
]);
}
public function getRowCount(): int
{
return $this->rows;
}
public function rules(): array
{
return [
'*.emailaddress' => ['email', 'unique:emails,emailaddress']
];
}
}
My Multisheet import class MultisheetContactsImport.php
class MultisheetContactsImport extends ContactsImport implements WithMultipleSheets
{
public function sheets(): array
{
return [
'Contacts' => new ContactsImport()
];
}
}
The methods failures and and getRowCount work fine if I use the ContactsImport class without multisheets however now I only get a response 0
Just Wrap your import line code with try catch block it should handle every thing:
try {
$master = Excel::import(new MasterImport($auth), $request->file('master_upload'));
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
$failures = $e->failures();
dd($failures);
foreach ($failures as $failure) {
$failure->row(); // row that went wrong
$failure->attribute(); // either heading key (if using heading row concern) or column index
$failure->errors(); // Actual error messages from Laravel validator
$failure->values(); // The values of the row that has failed.
}
}
You can add this method on you ImportClass
/**
* #param Failure ...$failures
* #throws ValidationException
*/
public function onFailure(Failure ...$failures)
{
$exception = ValidationException::withMessages(collect($failures)->map->toArray()->all());
throw $exception;
}
User Laravel Validation Exeption
use Illuminate\Validation\ValidationException;
and than on the view display validation messages:
#if(count($errors->getMessages()) > 0)
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>Validation Errors:</strong>
<ul>
#foreach($errors->getMessages() as $errorMessages)
#foreach($errorMessages as $errorMessage)
<li>
{{ $errorMessage }}
×
</li>
#endforeach
#endforeach
</ul>
</div>#endif
I'm using This Package Maatwebsite/Laravel-Excel to import data from excel file
what I wanna do is check if the column exists before import data from excel file,
if so, then update otherwise insert
Model:
<?php
namespace App\Imports;
use App\Medicine;
use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
HeadingRowFormatter::default('none');
class MedicineImport implements ToModel, WithHeadingRow
{
protected $company_id;
public function __construct($company_id)
{
$this->company_id = $company_id;
}
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
$expire_date = empty($row['Expire Date']) ? $row['Expire Date'] : Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['Expire Date']));
return new Medicine([
'name' => $row['Name'],
'price' => $row['Price'],
'expire_date' => $expire_date,
'company_id' => $this->company_id,
]);
}
}
Controller:
$company = Company::where('id',$id)->first();
$medicines=DB::table('medicines')->where('company_id', $id)->delete();
$company_id= $id;
Excel::import(new MedicineImport($company_id),request()->file('file'));
return redirect()->route('company.medicine.index',$company_id);
any ideas to do this?
You need to check just before insert, example:
public function model(array $row)
{
$exists = Medicine::where('name',$row['Name'])->where('company_id',$this->company_id)->first();
if ($exists) {
//LOGIC HERE TO UPDATE
return null;
}
$expire_date = empty($row['Expire Date']) ? $row['Expire Date'] : Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['Expire Date']));
return new Medicine([
'name' => $row['Name'],
'price' => $row['Price'],
'expire_date' => $expire_date,
'company_id' => $this->company_id,
]);
}
Itamar Garcia's answer is really a bad idea, I tried it in production and faced too many issues, loading took forever to finish, started getting lock timeout error etc. Anyway I ended up using WithUpserts, sorry to post this in a separate answer, stupid stackoverflow policy prohibits me from commenting on other answers until I reach 50 reputation.
use Maatwebsite\Excel\Concerns\WithUpserts;
class SubscribersImport implements ToModel, WithUpserts {
/**/
public function uniqueBy(){
/**/
return "email";
/**/
}
}
I am using vue with laravel. I am trying to save array of objects but not able to do it though i am able to save single object. Here is my code
App.vue
// Not working
saveData(){
this.axios.post('/addperson', **this.rows**).then((response) => {
console.log("WOW");
})
}
//working
saveData(){
this.axios.post('/addperson', **this.rows[0]**).then((response) => {
console.log("WOW");
})
}
Here is controller code where i am getting error when i pass array.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\OrderPerson;
class PersonController extends Controller
{
public function create(Request $request){
$person = new Person([
'name' => $request->get('name'),
'age' => $request->get('age'),
'user_id' =>$request->get('user_id')''
]);
$person->save();
return response()->json('Successfully added');
}
}
Can any body help me to save the array?
First of all assign all the this.rows to a data set then push it to the axios call something like this:
saveData(){
const postData = {
data: this.rows
}
this.axios.post('/addperson', postData).then((response) => {
console.log("WOW");
})
}
Now in laravel controller you can use foreach to use this data set.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\OrderPerson;
class PersonController extends Controller
{
public function create(Request $request){
foreach($request->data as $data)
{
$container = new Person([
'name' => $data['name'],
'age' => $data['age'],
'user_id' => $data['user_id']
]);
$container->save();
}
return response()->json('Successfully added');
}
}
Hope this helps.
I am trying to use the laravel 5.3 notification system. I have a many to many relationship on a couple of models. What I need to do is loop through all of the request data and send a notification to everyone appropriate. It seems that the notification methods won't work within a foreach loop. The error is:
BadMethodCallException in Builder.php line 2448:
Call to undefined method Illuminate\Database\Query\Builder::routeNotificationFor()
The code I am trying to figure out is:
public function storeHoursused(Request $request, Lessonhours $lessonhours)
{
$this->validate($request, [
'date_time' => 'required',
'numberofhours' => 'required|numeric',
'comments' => 'required|max:700'
]);
$hoursused = new Hoursused();
$hoursused->date_time = $request['date_time'];
$hoursused->numberofhours = $request['numberofhours'];
$hoursused->comments = $request['comments'];
$lessonhours->hoursused()->save($hoursused);
foreach($lessonhours->players as $player){
$player->users;
Notification::send($player, new HoursusedPosted($player->user));
//$lessonhours->player->notify(new HoursusedPosted($lessonhours->player->users));
}
return back()->with(['success' => 'Hours Used successfully added!']);
}
Is there a way to collect related data and pass to notification methods?
UPDATE:
The Players model looks like:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Collective\Html\Eloquent\FormAccessible;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Notifiable;
use Carbon\Carbon;
class Players extends Model
{
public $table = "players";
protected $fillable = array('fname', 'lname', 'gender', 'birthdate');
public function users()
{
return $this->belongsTo('App\User', 'users_id');
}
public function lessonhours()
{
return $this->belongsToMany('App\Lessonhours', 'lessonhour_player', 'players_id', 'lessonhours_id')
->withTimestamps();
}
public function getFullName($id)
{
return ucfirst($this->fname ) . ' ' . ucfirst($this->lname);
}
protected $dates = ['birthdate'];
protected $touches = ['lessonhours'];
public function setBirthdateAttribute($value)
{
$this->attributes['birthdate'] = Carbon::createFromFormat('m/d/Y', $value);
}
}
Your $player model needs to use the Illuminate\Notifications\Notifiable trait.