Загрузить CSV-файл Voyager

Я новичок в voyager, есть ли способ загружать пользователей через CSV в voyager. Я предполагаю, что мне придется сделать собственный шаблон для этого, любой, кто может указать мне правильное направление в этом, я был бы очень признателен.


person Jayjay    schedule 04.06.2018    source источник


Ответы (1)


Я знаю, что это старый вопрос, но, поскольку это первое, с чем я сталкиваюсь, когда исследую одно и то же, я хотел добавить ответ, чтобы те, кто приходит сюда, могли извлечь пользу.

Это решение для Laravel 8.9.0 и Voyager 1.4

У этого есть выбор перед загрузкой CSV, это не обязательно, но полезно, поэтому вам не нужно настраивать CSV перед загрузкой.

Я использовал для этого другой ресурс, который был в Laravel 5.5, вы можете увидеть исходные коды из Импорт Laravel-5.5 CSV

Я также добавлю ответвление, которое будет обновлено до Laravel 9.0 после того, как я закончу свой собственный проект.

Во всяком случае, я использую Laravel-Excel 3.1.24 для чтения CSV. Ресурс использовал более старую версию.

Начнем кодирование

Прежде чем соединить его с Voyager, сначала вы должны знать, что вам нужно написать свое представление и контроллер, чтобы выполнить эту работу.

Также нам понадобится наша модель. Для вашей модели пользователя убедитесь, что вы добавили все необходимые поля. Создайте миграцию с теми же потребностями.

Например, если ваша модель пользователя имеет это;

protected $fillable = [
    'name',
    'email',
    'password',
];

Убедитесь, что ваша миграция;

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
       }

После того, как вы настроите свою модель и выполните миграцию, вам понадобится еще одна модель для хранения CSV-файла. Это будет полезно для вас при выборе полей CSV и базы данных. В нем будет всего три поля; 'csv_filename', 'csv_header', 'csv_data' .

После настройки наших моделей нам нужно добавить config/app.php к нашему выбору.

    'db_fields' => [
    'name',
    'email',
    'password',
]

Эти поля мы получим из CSV, вы можете удалить пароль, если у вас нет пароля в вашем CSV файле.

Прежде чем перейти к нашему контроллеру, мы должны добавить Laravel-Excel. Из документации все, что вам нужно сделать, это запустить эту команду на терминале.

composer require "maatwebsite/excel:3.1.24"

Это добавит Laravel-Excel в наш проект. Нашему контроллеру понадобится импорт, который является классом, помогающим нам импортировать файлы, поставляется с Laravel-Excel. Для этого;

php artisan make:import CsvImport --model=User

Вы можете использовать его для быстрого старта, как рекомендуется в документации Laravel-Excel. Теперь мы можем попасть в наш контроллер. Мы создадим контроллер через команды Laravel. Нам понадобится куча вещей;

 use App\Imports\CsvImport; //This is needed for Laravel-Excel.
 use Illuminate\Http\Request; 
 use App\Models\User;  //Our user model.
 use App\Models\CsvData; //Our model for csv.
 use App\Http\Requests\CsvImportRequest; //I'll share this in a sec.
 use Maatwebsite\Excel\Facades\Excel; //This is from Laravel-Excel
 use Illuminate\Support\Str; //This is for basic password creation

 class ImportController extends Controller
 {

Нам нужны следующие функции;

    public function form()
{
    return view('import.form');
}

Эта функция довольно проста. Это вернет наше представление. Второй — когда мы анализируем наш CSV и сохраняем в нем добавление CSV_Data. Но сначала нам нужен запрос, как хотел Laravel.

 php artisan make:request CSVImportRequest

я поделюсь своим;

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

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'csv_file' => 'required|file'
    ];
}

Теперь мы можем вернуться к нашему контроллеру;

    public function parseImport(CsvImportRequest $request)
{
    //we getting with the request the file. So you need to create request with 
 //Laravel. And you should add this to your Controller as use App\Http\Requests\CsvImportRequest;

    $path = $request->file('csv_file')->getRealPath();
    if ($request->has('header')) {
        //this is coming from Laravel-Excel package. Make sure you added to your 
//controller; use Maatwebsite\Excel\Facades\Excel;
        $data = Excel::toArray(new CsvImport, request()->file('csv_file'))[0];
    } else {
        $data = array_map('str_getcsv', file($path));
    }

    if (count($data) > 0) {
       //checking if the header option selected
        if ($request->has('header')) {
            $csv_header_fields = [];
            foreach ($data[0] as $key => $value) {
                $csv_header_fields[] = $key;
            }
        }
        $csv_data = array_slice($data, 0, 2);
         //creating csvdata for our database
        $csv_data_file = CsvData::create([
            'csv_filename' => $request->file('csv_file')->getClientOriginalName(),
            'csv_header' => $request->has('header'),
            'csv_data' => json_encode($data)
        ]);
    } else {
        return redirect()->back();
    }
    //this is the view when we go after we submit our form.We're sending our data so we can select to match with db_fields.
    return view('import.fields', compact('csv_header_fields', 'csv_data', 'csv_data_file'));

}

А теперь функция импорта.

    public function processImport(Request $request)
{//we are getting data from request to match the fields.
    $data = CsvData::find($request->csv_data_file_id);
    $csv_data = json_decode($data->csv_data, true);
    $request->fields = array_flip($request->fields);
    foreach ($csv_data as $row) {
        $contact = new User();
        foreach (config('app.db_fields') as $index => $field) {
        //using config app.db_fields while matching with request fields
            if ($data->csv_header) {
                if ($field == "null") {
                    continue;
                } else if ($field == "password") {
                      //this is checkin if password option is set. If not, it is creating a password. You can eliminate this according to your needs.
                    if (isset($request->fields['password'])) {
                        $pw = $row[$request->fields['password']];
                    } else {
                        $pw = Str::random(10);
                    }
                } else
                    $contact->$field = $row[$request->fields[$field]];
            } else 
//same with the if but without headers. You can create a function to avoid writing 
//codes twice.
{
                if ($field == "null") {
                    continue;
                } else if ($field == "password") {
                    if (isset($request->fields['password'])) {
                        $pw = $row[$request->fields['password']];
                    } else {
                        $pw = Str::random(10);
                    }
                } else
                    $contact->$field = $row[$request->fields[$index]];

            }
        }
        $user = User::where(['email' => $contact->email])->first();
 //checking for duplicate
        if (empty($user)) {
            $contact->password = bcrypt($pw);
            $contact->save();

        } else {
            $duplicated[] = $contact->email;
            //if you want you can keep the duplicated ones to check which ones are duplicated
        }

    }
 //you can redirect wherever you want. I didn't need an success view so I returned 
//voyagers original users view to see my data.
    return redirect(route('voyager.users.index'));
}

Теперь мы можем создавать наши маршруты. Поскольку мы используем Voyager, не забудьте добавить voyager., чтобы мы могли использовать его с панелью администратора voyager.

 use App\Http\Controllers\ImportController;

 Route::group(['prefix' => 'admin','as' => 'voyager.', 'middleware' => 'admin.user'], 
function()
{
Route::get('import',[ImportController::class, 'form'])->name("import.form");
Route::post('import/parse', [ImportController::class, 'parseImport'])- 
>name("import.parse");
Route::post('import/process', [ImportController::class, 'processImport'])- 
>name("import.process");
});

Кроме того, нам понадобится представление при импорте и выборе. Я создал свои представления в файле views/import.

Для вашего лезвия формы вы хотите использовать его с вояджером, поэтому вам следует

@extends('voyager::master')

используйте это, чтобы иметь ту же тему. После этого вам нужно добавить свой html с разделом.

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">CSV Import</div>

                <div class="panel-body">
                    <form class="form-horizontal" method="POST" action="{{ route('voyager.import.parse') }}" enctype="multipart/form-data">
                        {{ csrf_field() }}

                        <div class="form-group{{ $errors->has('csv_file') ? ' has-error' : '' }}">
                            <label for="csv_file" class="col-md-4 control-label">CSV file to import</label>

                            <div class="col-md-6">
                                <input id="csv_file" type="file" class="form-control" name="csv_file" required>

                                @if ($errors->has('csv_file'))
                                    <span class="help-block">
                                    <strong>{{ $errors->first('csv_file') }}</strong>
                                </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <div class="checkbox">
                                    <label>
                                        <input type="checkbox" name="header" checked> File contains header row?
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-8 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    Parse CSV
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Как видите, мы используем voyager. чтобы добавить наш маршрут. В этом нам также помогут настройки Voyager. Во втором представлении мы выбираем наши db_fields.

@extends('voyager::master')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">CSV Import</div>
                <div class="panel-body">
                    <form class="form-horizontal" method="POST" action="{{ route('voyager.import.process') }}">
                        {{ csrf_field() }}
                        <input type="hidden" name="csv_data_file_id" value="{{ $csv_data_file->id }}" />

                        <table class="table">
                            @if (isset($csv_header_fields))
                                <tr>
                                    @foreach ($csv_header_fields as $csv_header_field)
                                        <th>{{ $csv_header_field }}</th>
                                    @endforeach
                                </tr>
                            @endif
                            @foreach ($csv_data as $row)
                                <tr>
                                    @foreach ($row as $key => $value)
                                        <td>{{ $value }}</td>
                                    @endforeach
                                </tr>
                            @endforeach
                            <tr>
                                @foreach ($csv_data[0] as $key => $value)
                                    <td>
                                        <select name="fields[{{ $key }}]">
                                            <option value="null">Do Not Save</option>
                                        @foreach (config('app.db_fields') as $db_field)
                                                <option value="{{ (\Request::has('header')) ? $db_field : $loop->index }}"
                                                        @if ($key === $db_field) selected @endif>{{ $db_field }}</option>
                                            @endforeach
                                        </select>
                                    </td>
                                @endforeach
                            </tr>
                        </table>

                        <button type="submit" class="btn btn-primary">
                            Import Data
                        </button>
                    </form>
                </div>
            </div>

        </div>
    </div>
</div>
@endsection

Это оно! Теперь у нас есть набор для импорта. Нам просто нужно настроить Voyager.

НАСТРОЙКИ ВОЯДЖЕРА

Во-первых, нам нужен пункт меню для доступа к нашему представлению импорта. После того, как вы попали в панель Voyager, вам нужно перейти в конструктор меню, чтобы создать пункт меню. Все, что вам нужно сделать, это сделать URL-адрес элемента как /admin/import с этим, когда вы щелкнете по этому элементу, вы перейдете к нашему представлению формы импорта. Что касается других параметров, вы можете изменить их по своему усмотрению.

Вам также может потребоваться изменить свою пользовательскую модель BREAD на App\Models\User, чтобы она могла получать все созданные нами поля.

laravel-8.9.0 voyager-1.4 laravel-excel-3.1 .24

person Enise İrem Colak    schedule 31.10.2020