phpvms/app/Services/ImportService.php
2026-06-01 18:38:58 -05:00

226 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Contracts\ImportExport;
use App\Contracts\Service;
use App\Models\Aircraft;
use App\Models\Airport;
use App\Models\Expense;
use App\Models\Fare;
use App\Models\Flight;
use App\Models\FlightFieldValue;
use App\Models\Subfleet;
use App\Services\ImportExport\AircraftImporter;
use App\Services\ImportExport\AirportImporter;
use App\Services\ImportExport\ExpenseImporter;
use App\Services\ImportExport\FareImporter;
use App\Services\ImportExport\FlightImporter;
use App\Services\ImportExport\SubfleetImporter;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Csv\Exception;
use League\Csv\Reader;
class ImportService extends Service
{
/**
* Throw a validation error back up because it will automatically show
* itself under the CSV file upload, and nothing special needs to be done
*
*
* @throws ValidationException
*/
protected function throwError($error, ?\Exception $e = null): void
{
Log::error($error);
if ($e instanceof \Exception) {
Log::error($e->getMessage());
}
$validator = Validator::make([], []);
$validator->errors()->add('csv_file', $error);
throw new ValidationException($validator);
}
/**
* @throws ValidationException
*/
public function openCsv(string $csv_file): ?Reader
{
try {
$reader = Reader::createFromPath($csv_file, 'r');
$reader->setDelimiter(',');
$reader->setHeaderOffset(0);
$reader->setEnclosure('"');
$reader->setEscape('\\');
return $reader;
} catch (Exception $exception) {
$this->throwError('Error opening CSV: '.$exception->getMessage(), $exception);
}
return null;
}
/**
* Run the actual importer, pass in one of the Import classes which implements
* the ImportExport interface
*
*
* @throws ValidationException
*/
protected function runImport(string $file_path, ImportExport $importer): array
{
$reader = $this->openCsv($file_path);
array_keys($importer->getColumns());
$header_rows = $reader->getHeader();
$records = $reader->getRecords($header_rows);
foreach ($records as $offset => $row) {
// turn it into a collection and run some filtering
$row = collect($row)->map(function ($val, $index): ?string {
$val = trim($val);
$val = str_ireplace(['\\n', '\\r'], '', $val);
return $val === '' ? null : $val;
})->toArray();
// Try to validate
$validator = Validator::make($row, $importer->getColumns());
if ($validator->fails()) {
$errors = 'Error in row '.$offset.','.implode(';', $validator->errors()->all());
$importer->errorLog($errors);
continue;
}
$importer->import($row, $offset);
}
return $importer->status;
}
/**
* Import aircraft
*
*
* @throws ValidationException
*/
public function importAircraft(string $csv_file, bool $delete_previous = true): array
{
if ($delete_previous) {
Aircraft::truncate();
// Log::warning('Aircraft table truncated by User: '.Auth::id().' , '.Auth::user()->name_private);
}
$importer = new AircraftImporter();
return $this->runImport($csv_file, $importer);
}
/**
* Import airports
*
*
* @throws ValidationException
*/
public function importAirports(string $csv_file, bool $delete_previous = true): array
{
if ($delete_previous) {
Airport::truncate();
}
$importer = new AirportImporter();
return $this->runImport($csv_file, $importer);
}
/**
* Import expenses
*
*
* @throws ValidationException
*/
public function importExpenses(string $csv_file, bool $delete_previous = true): array
{
if ($delete_previous) {
Expense::truncate();
}
$importer = new ExpenseImporter();
return $this->runImport($csv_file, $importer);
}
/**
* Import fares
*
*
* @throws ValidationException
*/
public function importFares(string $csv_file, bool $delete_previous = true): array
{
if ($delete_previous) {
Fare::truncate();
}
$importer = new FareImporter();
return $this->runImport($csv_file, $importer);
}
/**
* Import flights
*
*
* @throws ValidationException
*/
public function importFlights(string $csv_file, ?string $delete_previous = null): array
{
if (!in_array($delete_previous, [null, '', '0'], true)) {
// If delete_previous contains all, then delete everything
if ($delete_previous === 'all') {
Flight::truncate();
FlightFieldValue::truncate();
} elseif ($delete_previous === 'core') {
// Delete all flights where the owner_type is null
Flight::whereNull('owner_type')->delete();
}
}
$importer = new FlightImporter();
return $this->runImport($csv_file, $importer);
}
/**
* Import subfleets
*
*
* @throws ValidationException
*/
public function importSubfleets(string $csv_file, bool $delete_previous = true): array
{
if ($delete_previous) {
Subfleet::truncate();
DB::table('subfleet_rank')->truncate();
DB::table('flight_subfleet')->truncate();
DB::table('typerating_subfleet')->truncate();
// Log::warning('Subfleet and tied relationship tables truncated by User: '.Auth::id().' , '.Auth::user()->name_private);
}
$importer = new SubfleetImporter();
return $this->runImport($csv_file, $importer);
}
}