CSV上传Voyager

时间:2018-06-04 12:04:06

标签: php laravel-5.5 voyager

我是旅行者的新手,有没有办法在旅行者中通过CSV上传用户。我想我必须为此制作一个自定义模板,任何能指出我正确方向的人,我都会非常感激。

1 个答案:

答案 0 :(得分:0)

我知道这是一个古老的问题,但是由于这是我研究同一件事时遇到的第一件事,因此我想添加一个答案,以便那些来这里的人能够受益。 < / p>

此解决方案适用于Laravel 8.9.0和Voyager 1.4

在上传CSV之前有一个选择,这不是必需的,但是很好,因此您不必在上传前调整CSV。

我确实使用了另一种资源,它在 Laravel 5.5 中,您可以从Laravel-5.5 CSV Import

查看原始代码

完成我自己的项目后,我还将添加一个用Laravel 9.0更新的fork。

无论如何,我使用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文档一样。现在我们可以进入控制器了。我们将创建控制器vie 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");
});

此外,我们在导入和选择时需要一个视图。我在视图/导入中创建了视图。

对于表单刀片,您希望将其与voyager一起使用,因此您应该

@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设置。我们的第二个视图是我们选择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进行配置。

旅行者设置

首先,我们需要一个菜单​​项才能进入导入视图。进入“旅行者”面板后,您需要转到菜单构建器来创建菜单项。您所要做的就是将项目的网址设置为/ admin / import,单击该项目时,将转到我们的导入表单视图。对于其他选项,您可以根据需要更改它们。

您可能还需要从用户BREAD Model更改为App \ Models \ User,以便它可以接收我们创建的所有字段。