Загрузка и парсинг CSV в Ларавел

Задача простая — загрузить CSV и разпарсить его. Кроме того нужно сохраните этот файл и сделать запись, каким пользователем это было сделано. Приступим.

Код метода в контроллере, я объясню его чуть ниже:

public function importProcess(UploadImportModelRequest $request)
{
    $originalFile   = $request->file('import_file');
    $import = Import::create([
        'filename' => $originalFile->getClientOriginalName(),
        'user_id' => auth()->id(),
    ]);

    $file = $import->addMediaFromRequest('import_file')
        ->toMediaCollection('imports');

    $filename = storage_path('app/' . $file->id . '/' . $file->file_name);
    $reader = new \SpreadsheetReader($filename);
    foreach ($reader as $row) {
        // Парсим строки...
    }
}

Пояснение 1. Валидация FormRequest

Видите класс UploadImportModelRequest в параметрах? Это помогает нам проверить, существует ли файл, и является ли он CSV или TXT.

app/Http/Requests/UploadImportModelRequest.php:

class UploadImportModelRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'import_file' => [
                'required',
                'file',
                'mimes:csv,txt'
            ],
        ];
    }
}

Пояснение 2. Загрузить файл при помощи Laravel MediaLibrary

Этот пакет для загрузки файлов в Laravel самый популярный и вот почему — вы можете сделать массу вещей всего всего лишь несколькими строками кода.

Но для начала нам нужно сохранить запись импорта и получить для нее объект модели Eloquent:

$originalFile   = $request->file('import_file');
$import = Import::create([
    'filename' => $originalFile->getClientOriginalName(),
    'user_id' => auth()->id(),
]);

Поэтому мы сохраняем исходное имя файла, каким бы оно ни было, например «dummy import test.csv» или что-то в этом роде. Позже я покажу, почему этот «оригинал» важен.

Результатом этого Eloquent запроса является объект $import, и здесь пригодится Laravel MediaLibrary — одной строкой кода мы можем сохранить файл в папку, а также назначить этот файл объекту $import:

$file = $import->addMediaFromRequest('import_file')
    ->toMediaCollection('imports');

В результата имеем:

  • Новая запись БД, хранящаяся в таблице «media» с именем файла и отношением к объекту $import;
  • Папка, созданная в storage/app, с идентификатором этой «медиа» записи;
  • Внутри этой новой папки хранится загруженный файл;
  • Этот «медиа» объект возвращается в $file.

Пояснение 3. Получение РЕАЛЬНОГО имени файла и парсинг CSV

Важно, что Laravel MediaLibrary фактически переименовывает файл, чтобы сделать его «sluggable» (ЧПУ), поэтому фактическое имя файла отличается от исходного. Смотрите разницу в имени файла и записи в таблице БД «медиа»:

Laravel MediaLibrary

Вот почему нам нужно прочитать файл CSV с новым именем, хранящимся внутри объекта $file.

$filename = storage_path('app/' . $file->id . '/' . $file->file_name);
$reader = new \SpreadsheetReader($filename);
foreach ($reader as $row) {
    // Парсим строки...
}

Как видите, мы теперь обращаемся к файлу как storage/[media.id]/[media.file_name].

В этом примере я не буду подробно разбирать парсинг CSV, поскольку он очень индивидуален, но я рекомендую использовать пакет Spreadsheet Reader — он довольно старый, но по некоторым причинам работает гораздо быстрее, чем Laravel Excel или другие.

Автор: Povilas Korop
Перевод: Demiurge Ash