Closed zanozik closed 5 years ago
Thanks for submitting the ticket. Unfortunately the information you provided is incomplete. We need to know which version you use and how to reproduce it. Please include code examples. Before we can pick it up, please check (https://github.com/Maatwebsite/Laravel-Excel/blob/3.0/.github/ISSUE_TEMPLATE.md) and add the missing information. To make processing of this ticket a lot easier, please make sure to check (https://laravel-excel.maatwebsite.nl/docs/3.0/getting-started/contributing) and double-check if you have filled in the issue template correctly. This will allow us to pick up your ticket more efficiently. Issues that follow the guidelines correctly will get priority over other issues.
Please provide more information according to the issue template.
The following should work:
$headings = (new HeadingRowImport)->toArray('users.xlsx');
/**
* Perform whatever validation on the headings here
*/
Excel::import(new UsersImport, 'users.xlsx');
return redirect('/')->with('success', 'All good!');
Not work when you let user upload the document, and you want validate $request->file
, because the file once was read by HeadingRowImport
is remove from temporary space.
You need to store the file and then validate.
In this situation I'd suggest storing the file to a disk, and using that file instead of using the uploaded file twice.
Thank you for answers! Since moving a file to storage was extremely inconvenient for the project, I have designed the workflow differently by subscribing with Reader::listen
globally (validation by header used project-wide).
Could share my code if anyone interested.
@zanozik sounds like a nice solution as well :) Feel free to share! Can definitely help someone!
So this is the most cleanest project-wide way I could come up with:
Create regular import file:
namespace App\Imports;
use App\Models\Transports\GasStations\GasStation;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class GasStationImport implements ToModel, WithHeadingRow
{
public $model = GasStation::class; // Only needed for globalization purpose
// Excel file header
public $header = [
'name', 'country', 'city','address', 'postal', 'zone', 'latitude', 'longitude'
];
public $verifyHeader = true; // Header verification toggle
public $truncate = true; // We want to truncate table before the import
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new $this->model($row); // We have matching table fields, so we can just do that
// return new GasStation($row); // If you don't need globalization
}
}
And in app\Providers\AppServiceProvider.php
:
namespace App\Providers;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Events\BeforeImport;
use Maatwebsite\Excel\Imports\HeadingRowExtractor;
use Maatwebsite\Excel\Reader;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
Reader::listen(BeforeImport::class, function (BeforeImport $event) {
$concernable = $event->getConcernable();
if ($concernable->verifyHeader) {
//Extracting and "truncating" header from Excel's active worksheet (assuming import is only single worksheet)
$header = array_filter(HeadingRowExtractor::extract($event->getDelegate()->getDelegate()->getActiveSheet(), $concernable));
//Checking if headers match and throwing ValidationException otherwise
throw_unless($header === $concernable->header, ValidationException::withMessages(['message' => trans('validation.unknown_file_template')]));
}
//This is where GasStationImport's $truncate and $model come into play. You can put your logic here.
if($concernable->truncate) {
$concernable->model::truncate();
}
});
}
}
In your controller, a method could look like this:
public function import(Request $request)
{
$this->validate($request, [
'file' => 'required|file|max:1024|mimes:xls,xlsx'
]);
\Excel::import(new GasStationImport(), $request->file('file'));
return response()->json(['message' => trans('app.import_successful')]);
}
Typos edited
Awesome, looks good @zanozik
This doesn't appear to work anymore but I got it working with:
BeforeSheet::class => function(BeforeSheet $event) {
$concernable = $event->getConcernable();
if (@$concernable->verifyHeadings) {
//Extracting and "truncating" header from Excel's worksheet
$headings = array_filter(HeadingRowExtractor::extract($event->getSheet()->getDelegate(), $concernable));
array_splice($headings, count($concernable->expectedHeadings));
//Checking if headers match and throwing ValidationException otherwise
throw_unless($headings === $concernable->expectedHeadings, ValidationException::withMessages(['message' => trans('validation.invalid_headings')]));
}
},
When using
HeadingRowImport
for file validation, as suggested here it's impossible to run Excel::import() with the same uploaded file, since it delete it.