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";
/**/
}
}
Related
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;
}
}
Please tell me, how create a new column when exporting(to exel) table. There is a table in DB of this kind:
I installed package for export maatwebsite / excel. There is also a my file of model:
class ScheduledInspectionModel extends Model
{
protected $table = 'scheduled_inspection'; // table name
protected $fillable = ['name_smp', 'name_control', "verification_start", "verification_end", 'verification_duration'];
public $timestamps = false;
}
Controller:
class OrganizationsExportController extends Controller
{
public function export()
{
return (new OrganizationsExport)->download('organizations_export.xls');
}
}
And file with export description:
class OrganizationsExport implements FromCollection, ShouldAutoSize, WithHeadings, WithEvents
{
use Exportable;
/**
* #return \Illuminate\Support\Collection
*/
public function collection()
{
return ScheduledInspectionModel::all();
}
public function headings(): array
{
return [
'id',
'Name SMP',
'Name Control',
'Verification Start',
'Verification End',
'Verification Duration'
];
}
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
$event->sheet->getStyle('A1:F1')->applyFromArray([
'font' => [
'bold' => true
]
]);
}
];
}
}
The exported table looks like this:
The export works :) But I want to create in place of the 'id' column (I can exclude it using map ()), 'Number' column and enter the line numbering accordingly. Please tell me how to do this?
I would use the map() function on the export, here you can tweak the source of each column. I assumed column names in your db, due to not knowing the structure. Add one to the count each time it is transformed and you should be golden, which is done with the ++ operator.
private $count = 0;
public function map(ScheduledInspectionModel $inspection): array
{
return [
++$this->count,
$inspection->name_smp,
$inspection->name_control,
$inspection->verification_start->format('Y-m-d') . ' - ' .$inspection->verification_end->format('Y-m-d'),
$inspection->duration,
];
}
To call format on dates, you have to set the dates array on your model.
class ScheduledInspectionModel {
protected $dates = [
'verification_start',
'verification_end',
];
}
I have a problem to skip the row about importing excel laravel with the Maatwebsite / Laravel-Excel package.
I tried many ways from the internet but it still didn't work.
this is my controller code
if ($request->hasFile('file')) {
$import = new HsatuImport();
$file = $request->file('file'); //GET FILE
// config(['excel.import.startRow' => 2]);
Excel::import($import, $file)->limit(false, 2); //IMPORT FILE
return redirect()->route('admin.hsatus.upload')->withFlashSuccess('Upload Berhasil '.$import->getRowCount().' Data');
} else {
return redirect()->route('admin.hsatus.upload')->withFlashSuccess('Upload Gagal');
}
and this is my import code
<?php
namespace App\Imports;
use App\Models\Hsatu;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\ToModel;
class HsatuImport implements ToModel
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
private $rows = 0;
public function model(array $row)
{
++$this->rows;
return new Hsatu([
'region' => #$row[0],
'nomor_faktur' => #$row[1],
'nomor_rangka' => #$row[2],
'kode_mesin' => #$row[3]
]);
}
public function headingRow(): int
{
return 1;
}
public function getRowCount(): int
{
return $this->rows;
}
}
I had the problem with skipping when there's already existing Eloquent model. So below my code:
public function model(array $row)
{
$name = $row['name'];
if (!Category::where('name', $name)->exists())
return new Category([
'name' => $name
]);
}
I'm returning Model instance only if given statement is true.
When I run the code I get no error but the data I am trying to display is not displaying it's just blank.. can someone tell me what I'm doing wrong?
My controller:
public function openingPage($id) {
$this->getGames();
$games = $this->getGames();
return view('caseopener')->with('games',$games);
}
private function getGames() {
$games = array();
foreach ($this->data->items as $item) {
$game = new Game($item);
$games[] = array(
'id' => $game['id'],
'name' => $game['name'],
'price' => $game['price'],
'image' => $game['image'],
);
}
return $games;
}
The 'Game' Model that is used in 'getGames function':
class Game extends Model
{
private $id;
public $data;
public function __construct($id) {
parent::__construct();
$this->id = $id;
$this->data = $this->getData();
}
private function getData() {
$game = DB::table('products')->where('id', 1)->first();
if(empty($game)) return array();
return $game;
}
}
The view:
#foreach ($games as $game)
<div class="gold">$ {{ $game['price'] }}</div>
#endforeach
I think you are over-complicating things. You could simplify your flow like this:
Given your provided code, it seems like you are using a custom table name ('products') in your Game model. So we'll address this first:
Game.php
class Game extends Model
{
protected $table = 'products'; //
}
Now, it seems like you're searching an array of Game ids ($this->data->items). If so, you could make use of Eloquent for your query, specially the whereIn() method:
YourController.php
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', $games);
}
Optionally, if you want to make sure of just returning the id, name, price and image of each Game/product, you could format the response with API Resources:
php artisan make:resource GameResource
Then in your newly created class:
app/Http/Resources/GameResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GameResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'image' => $this->image,
];
}
}
So now just update your controller:
YourController.php
use App\Http\Resources\GameResource;
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', GameResource::collection($games));
} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I have a basic form that contains an excel file upload and a select option.
The select contains a few options. I want to pass the selected option from the user with the file to my Excel::import
I tried passing another parameter to my import but it returns error. (has to be string, array given)
Is this possible using Laravel excel import?
Controller
public function create(Request $request)
{
if($request->hasFile('file'))
{
$package = $request->input('package');
// how can I add $package to my import to insert it to my user model?
Excel::import(new AccountsImport, $request->file('file'));
return ['message' => 'Excel has been succesfully uploaded'];
}
}
AccountsImport
class AccountsImport implements ToCollection, withHeadingRow
{
public function collection(Collection $rows)
{
$rows->each(function($row, $key) {
Account::create([
'name' => $row['student'],
'email' => $row['e_mail'],
// Want it to be possible to add my package here
//'package' => $package
]);
});
}
}
Maybe you could pass down custom data to your AccountsImport class?
You would have a data array as such:
$data = [
'package' => $package,
// other data here
];
You would then do something like this:
Excel::import(new AccountsImport($data), $request->file('file'));
That would require some changes in your import class.
class AccountsImport implements ToCollection, withHeadingRow
{
private $data;
public function __construct(array $data = [])
{
$this->data = $data;
}
public function collection(Collection $rows)
{
$rows->each(function($row, $key) {
Account::create(array_merge([
'name' => $row['student'],
'email' => $row['e_mail'],
// Want it to be possible to add my package here
//'package' => $package
], $this->data));
});
}
}
I took a look at Laravel Excel's API and couldn't see something that would cater for this, but this should work.