first upload all files
This commit is contained in:
13
Modules/Import/Config/assets.php
Normal file
13
Modules/Import/Config/assets.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Define which assets will be available through the asset manager
|
||||
|--------------------------------------------------------------------------
|
||||
| These assets are registered on the asset manager
|
||||
*/
|
||||
'all_assets' => [
|
||||
'admin.import.js' => ['module' => 'import:admin/js/importer.js'],
|
||||
],
|
||||
];
|
||||
8
Modules/Import/Config/permissions.php
Normal file
8
Modules/Import/Config/permissions.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'admin.importer' => [
|
||||
'index' => 'import::permissions.index',
|
||||
'create' => 'import::permissions.create',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Http\Controllers\Admin;
|
||||
|
||||
class DownloadCsvController
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$import_types = ['product' => 'products.csv'];
|
||||
|
||||
if (array_key_exists(request('import_type'), $import_types)) {
|
||||
$path = storage_path('app/csv_templates/' . $import_types[request('import_type')]);
|
||||
|
||||
return response()->download($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Modules/Import/Http/Controllers/Admin/ImporterController.php
Normal file
44
Modules/Import/Http/Controllers/Admin/ImporterController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Http\Controllers\Admin;
|
||||
|
||||
use Maatwebsite\Excel\Excel;
|
||||
use Modules\Import\Imports\ProductImport;
|
||||
use Maatwebsite\Excel\Facades\Excel as ExcelFacade;
|
||||
use Modules\Import\Http\Requests\StoreImporterRequest;
|
||||
|
||||
class ImporterController
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('import::admin.importer.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Modules\Import\Http\Requests\StoreImporterRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreImporterRequest $request)
|
||||
{
|
||||
@set_time_limit(0);
|
||||
|
||||
$importers = ['product' => ProductImport::class];
|
||||
|
||||
ExcelFacade::import(new $importers[$request->import_type], $request->file('csv_file'), null, Excel::CSV);
|
||||
|
||||
if (session()->has('importer_errors')) {
|
||||
return back()->with('error', trans('import::messages.there_was_an_error_on_rows', [
|
||||
'rows' => implode(', ', session()->pull('importer_errors', [])),
|
||||
]));
|
||||
}
|
||||
|
||||
return back()->with('success', trans('import::messages.the_importer_has_been_run_successfully'));
|
||||
}
|
||||
}
|
||||
29
Modules/Import/Http/Requests/StoreImporterRequest.php
Normal file
29
Modules/Import/Http/Requests/StoreImporterRequest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Http\Requests;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Modules\Core\Http\Requests\Request;
|
||||
|
||||
class StoreImporterRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Available attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $availableAttributes = 'import::attributes';
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'import_type' => ['required', Rule::in(['product'])],
|
||||
'csv_file' => ['required', 'file', 'mimes:csv,txt'],
|
||||
];
|
||||
}
|
||||
}
|
||||
217
Modules/Import/Imports/ProductImport.php
Normal file
217
Modules/Import/Imports/ProductImport.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Imports;
|
||||
|
||||
use Maatwebsite\Excel\Row;
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Maatwebsite\Excel\Concerns\OnEachRow;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||
|
||||
class ProductImport implements OnEachRow, WithChunkReading, WithHeadingRow
|
||||
{
|
||||
public function chunkSize(): int
|
||||
{
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function onRow(Row $row)
|
||||
{
|
||||
$data = $this->normalize($row->toArray());
|
||||
|
||||
request()->merge($data);
|
||||
|
||||
try {
|
||||
Product::create($data);
|
||||
} catch (QueryException | ValidationException $e) {
|
||||
session()->push('importer_errors', $row->getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
private function normalize(array $data)
|
||||
{
|
||||
return array_filter([
|
||||
'name' => $data['name'],
|
||||
'sku' => $data['sku'],
|
||||
'description' => $data['description'],
|
||||
'short_description' => $data['short_description'],
|
||||
'is_active' => $data['active'],
|
||||
'brand_id' => $data['brand'],
|
||||
'categories' => $this->explode($data['categories']),
|
||||
'tax_class_id' => $data['tax_class'],
|
||||
'tags' => $this->explode($data['tags']),
|
||||
'price' => $data['price'],
|
||||
'special_price' => $data['special_price'],
|
||||
'special_price_type' => $data['special_price_type'],
|
||||
'special_price_start' => $data['special_price_start'],
|
||||
'special_price_end' => $data['special_price_end'],
|
||||
'manage_stock' => $data['manage_stock'] ?? 0, //bug fix
|
||||
'qty' => $data['quantity'],
|
||||
'in_stock' => $data['in_stock'] ?? 1, //bug fix
|
||||
'new_from' => $data['new_from'],
|
||||
'new_to' => $data['new_to'],
|
||||
'up_sells' => $this->explode($data['up_sells']),
|
||||
'cross_sells' => $this->explode($data['cross_sells']),
|
||||
'related_products' => $this->explode($data['related_products']),
|
||||
'files' => $this->normalizeFiles($data),
|
||||
'meta' => $this->normalizeMetaData($data),
|
||||
'attributes' => $this->normalizeAttributes($data),
|
||||
'options' => $this->normalizeOptions($data),
|
||||
], function ($value) {
|
||||
return $value || is_numeric($value);
|
||||
});
|
||||
}
|
||||
|
||||
private function explode($values)
|
||||
{
|
||||
if (trim($values) == '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_map('trim', explode(',', $values));
|
||||
}
|
||||
|
||||
private function normalizeFiles(array $data)
|
||||
{
|
||||
return [
|
||||
'base_image' => $data['base_image'],
|
||||
'additional_images' => $this->explode($data['additional_images']),
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeMetaData($data)
|
||||
{
|
||||
return [
|
||||
'meta_title' => $data['meta_title'],
|
||||
'meta_description' => $data['meta_description'],
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeAttributes(array $data)
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
foreach ($this->findAttributes($data) as $attributeNumber => $attributeId) {
|
||||
$attributes[] = [
|
||||
'attribute_id' => $attributeId,
|
||||
'values' => $this->findAttributeValues($data, $attributeNumber),
|
||||
];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
private function findAttributes(array $data)
|
||||
{
|
||||
return collect($data)->filter(function ($value, $column) {
|
||||
preg_match('/^attribute_\d$/', $column, $matches);
|
||||
|
||||
return ! empty($matches);
|
||||
})->filter();
|
||||
}
|
||||
|
||||
private function findAttributeValues(array $data, $attributeNumber)
|
||||
{
|
||||
return collect($data)->filter(function ($value, $column) use ($attributeNumber) {
|
||||
return $column === "{$attributeNumber}_values";
|
||||
})->map(function ($values) {
|
||||
return $this->explode($values);
|
||||
})->flatten()->toArray();
|
||||
}
|
||||
|
||||
private function normalizeOptions(array $data)
|
||||
{
|
||||
$options = [];
|
||||
|
||||
foreach ($this->findOptionPrefixes($data) as $optionPrefix) {
|
||||
$option = $this->findOptionAttributes($data, $optionPrefix);
|
||||
|
||||
if (is_null($option['name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[] = [
|
||||
'name' => $option['name'],
|
||||
'type' => $option['type'],
|
||||
'is_required' => $option['is_required'],
|
||||
'values' => $this->findOptionValues($option),
|
||||
];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function findOptionPrefixes(array $data)
|
||||
{
|
||||
return collect($data)->filter(function ($value, $column) {
|
||||
preg_match('/^option_\d_name$/', $column, $matches);
|
||||
|
||||
return ! empty($matches);
|
||||
})->keys()->map(function ($column) {
|
||||
return str_replace('_name', '', $column);
|
||||
});
|
||||
}
|
||||
|
||||
private function findOptionAttributes(array $data, $optionPrefix)
|
||||
{
|
||||
return collect($data)->filter(function ($value, $column) use ($optionPrefix) {
|
||||
preg_match("/{$optionPrefix}_.*/", $column, $matches);
|
||||
|
||||
return ! empty($matches);
|
||||
})->mapWithKeys(function ($value, $column) use ($optionPrefix) {
|
||||
$column = str_replace("{$optionPrefix}_", '', $column);
|
||||
|
||||
return [$column => $value];
|
||||
});
|
||||
}
|
||||
|
||||
private function findOptionValues(Collection $option)
|
||||
{
|
||||
$values = [];
|
||||
|
||||
foreach ($this->findOptionValuePrefixes($option) as $valuePrefix) {
|
||||
$value = $this->findOptionValueAttributes($option, $valuePrefix);
|
||||
|
||||
if (is_null($value['label'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$values[] = [
|
||||
'label' => $value['label'],
|
||||
'price' => $value['price'],
|
||||
'price_type' => $value['price_type'],
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function findOptionValuePrefixes(Collection $option)
|
||||
{
|
||||
return $option->filter(function ($value, $column) {
|
||||
preg_match('/value_\d_.+/', $column, $matches);
|
||||
|
||||
return ! empty($matches);
|
||||
})->keys()->map(function ($column) {
|
||||
preg_match('/value_\d/', $column, $matches);
|
||||
|
||||
return $matches[0];
|
||||
})->unique();
|
||||
}
|
||||
|
||||
private function findOptionValueAttributes(Collection $option, $valuePrefix)
|
||||
{
|
||||
return $option->filter(function ($value, $column) use ($valuePrefix) {
|
||||
preg_match("/{$valuePrefix}_.*/", $column, $matches);
|
||||
|
||||
return ! empty($matches);
|
||||
})->mapWithKeys(function ($value, $column) use ($valuePrefix) {
|
||||
$column = str_replace("{$valuePrefix}_", '', $column);
|
||||
|
||||
return [$column => $value];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
21
Modules/Import/Providers/ImportServiceProvider.php
Normal file
21
Modules/Import/Providers/ImportServiceProvider.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Providers;
|
||||
|
||||
use Modules\Support\Traits\AddsAsset;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ImportServiceProvider extends ServiceProvider
|
||||
{
|
||||
use AddsAsset;
|
||||
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->addAdminAssets('admin.importer.index', ['admin.import.js']);
|
||||
}
|
||||
}
|
||||
7
Modules/Import/Resources/assets/admin/js/main.js
Normal file
7
Modules/Import/Resources/assets/admin/js/main.js
Normal file
@@ -0,0 +1,7 @@
|
||||
$('.btn-actions').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
let importType = $('#import_type').val();
|
||||
|
||||
window.location.href = route('admin.download_csv.index', { import_type: importType });
|
||||
});
|
||||
6
Modules/Import/Resources/lang/en/attributes.php
Normal file
6
Modules/Import/Resources/lang/en/attributes.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'csv_file' => 'CSV File',
|
||||
'import_type' => 'Import Type',
|
||||
];
|
||||
11
Modules/Import/Resources/lang/en/importer.php
Normal file
11
Modules/Import/Resources/lang/en/importer.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'importer' => 'Importer',
|
||||
'download_csv' => 'Download CSV',
|
||||
'import' => 'Import',
|
||||
'import_types' => [
|
||||
'product' => 'Product',
|
||||
],
|
||||
'run' => 'Run',
|
||||
];
|
||||
6
Modules/Import/Resources/lang/en/messages.php
Normal file
6
Modules/Import/Resources/lang/en/messages.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'the_importer_has_been_run_successfully' => 'The importer has been run successfully.',
|
||||
'there_was_an_error_on_rows' => 'There was an error on rows (:rows).',
|
||||
];
|
||||
6
Modules/Import/Resources/lang/en/permissions.php
Normal file
6
Modules/Import/Resources/lang/en/permissions.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'index' => 'Index Import',
|
||||
'create' => 'Create Import',
|
||||
];
|
||||
5
Modules/Import/Resources/lang/en/sidebar.php
Normal file
5
Modules/Import/Resources/lang/en/sidebar.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'import' => 'Importer',
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('import::importer.importer'))
|
||||
|
||||
<li class="active">{{ trans('import::importer.importer') }}</li>
|
||||
@endcomponent
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="btn-group pull-right">
|
||||
<a href="#" class="btn btn-primary btn-actions">
|
||||
{{ trans('import::importer.download_csv') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.importer.store') }}" enctype="multipart/form-data" class="form-horizontal">
|
||||
@csrf
|
||||
|
||||
<div class="accordion-content">
|
||||
<div class="accordion-box-content clearfix">
|
||||
<div class="col-md-12">
|
||||
<div class="accordion-box-content">
|
||||
<div class="tab-content clearfix">
|
||||
<div class="tab-pane fade in active">
|
||||
<h3 class="tab-content-title">
|
||||
{{ trans('import::importer.import') }}
|
||||
</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
{{ Form::file('csv_file', trans('import::attributes.csv_file'), $errors, null, ['required' => true]) }}
|
||||
{{ Form::select('import_type', trans('import::attributes.import_type'), $errors, trans('import::importer.import_types'), null, ['required' => true]) }}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-3 col-md-10">
|
||||
<button type="submit" class="btn btn-primary" data-loading>
|
||||
{{ trans('import::importer.run') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
21
Modules/Import/Routes/admin.php
Normal file
21
Modules/Import/Routes/admin.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('importer', [
|
||||
'as' => 'admin.importer.index',
|
||||
'uses' => 'ImporterController@index',
|
||||
'middleware' => 'can:admin.importer.index',
|
||||
]);
|
||||
|
||||
Route::post('importer', [
|
||||
'as' => 'admin.importer.store',
|
||||
'uses' => 'ImporterController@store',
|
||||
'middleware' => 'can:admin.importer.create',
|
||||
]);
|
||||
|
||||
Route::get('download-csv', [
|
||||
'as' => 'admin.download_csv.index',
|
||||
'uses' => 'DownloadCsvController@index',
|
||||
'middleware' => 'can:admin.importer.index',
|
||||
]);
|
||||
26
Modules/Import/Sidebar/SidebarExtender.php
Normal file
26
Modules/Import/Sidebar/SidebarExtender.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Import\Sidebar;
|
||||
|
||||
use Maatwebsite\Sidebar\Item;
|
||||
use Maatwebsite\Sidebar\Menu;
|
||||
use Maatwebsite\Sidebar\Group;
|
||||
use Modules\Admin\Sidebar\BaseSidebarExtender;
|
||||
|
||||
class SidebarExtender extends BaseSidebarExtender
|
||||
{
|
||||
public function extend(Menu $menu)
|
||||
{
|
||||
$menu->group(trans('admin::sidebar.system'), function (Group $group) {
|
||||
$group->item(trans('admin::sidebar.tools'), function (Item $item) {
|
||||
$item->item(trans('import::sidebar.import'), function (Item $item) {
|
||||
$item->weight(5);
|
||||
$item->route('admin.importer.index');
|
||||
$item->authorize(
|
||||
$this->auth->hasAccess('admin.importer.index')
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
28
Modules/Import/composer.json
Normal file
28
Modules/Import/composer.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "fleetcart/import",
|
||||
"description": "The FleetCart Import Module.",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Envay Soft",
|
||||
"email": "envaysoft@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.2",
|
||||
"maatwebsite/excel": "^3.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Import\\": ""
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
9
Modules/Import/module.json
Normal file
9
Modules/Import/module.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "Import",
|
||||
"alias": "import",
|
||||
"description": "The FleetCart Import Module.",
|
||||
"priority": 100,
|
||||
"providers": [
|
||||
"Modules\\Import\\Providers\\ImportServiceProvider"
|
||||
]
|
||||
}
|
||||
3
Modules/Import/webpack.mix.js
Normal file
3
Modules/Import/webpack.mix.js
Normal file
@@ -0,0 +1,3 @@
|
||||
let mix = require('laravel-mix');
|
||||
|
||||
mix.js(`${__dirname}/Resources/assets/admin/js/main.js`, `${__dirname}/Assets/admin/js/importer.js`);
|
||||
Reference in New Issue
Block a user