我是 voyager 的新手,有没有办法可以在 voyager 中通过 CSV 上传用户。我想我必须为此制作一个自定义模板,任何可以为我指出正确方向的人,我将不胜感激。
1 回答
我知道这是一个老问题,但是由于这是我在研究同一件事时遇到的第一件事,所以我想添加一个答案,以便来这里的人受益。
此解决方案适用于 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 添加到我们的项目中。我们的控制器需要“import”,这是一个帮助我们导入文件的类,它是 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");
});
此外,我们在导入和选择时需要一个视图。我在视图/导入中创建了我的视图。
对于您的表单刀片,您希望将其与 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 进行配置。
航海者设置
首先,我们需要一个菜单项来访问我们的导入视图。进入 Voyager 面板后,您需要转到菜单生成器来创建菜单项。您所要做的就是将项目的 url 设置为 /admin/import,当您单击该项目时,您将转到我们的导入表单视图。对于其他选项,您可以随意更改它们。
您可能还需要将用户 BREAD Model 更改为 App\Models\User,以便它可以接收我们创建的所有字段。