Finish basic import workflow
This commit is contained in:
parent
d9b98bccca
commit
a9899ae3c1
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use Str;
|
||||
use Backend\Classes\ControllerBehavior;
|
||||
use League\Csv\Writer as CsvWrtier;
|
||||
use League\Csv\Writer as CsvWriter;
|
||||
use League\Csv\Reader as CsvReader;
|
||||
use ApplicationException;
|
||||
use Exception;
|
||||
|
|
@ -86,7 +86,22 @@ class ImportExportController extends ControllerBehavior
|
|||
|
||||
public function onImport()
|
||||
{
|
||||
// traceLog(post());
|
||||
try {
|
||||
$model = $this->importGetModel();
|
||||
$matches = post('column_match', []);
|
||||
$sessionKey = $this->importUploadFormWidget->getSessionKey();
|
||||
|
||||
$model->importDataFromColumnMatch($matches, $sessionKey, [
|
||||
'firstRowTitles' => post('first_row_titles', false)
|
||||
]);
|
||||
|
||||
$this->vars['importResults'] = $model->getResultStats();
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->controller->handleError($ex);
|
||||
}
|
||||
|
||||
return $this->importExportMakePartial('import_result_form');
|
||||
}
|
||||
|
||||
public function onImportLoadForm()
|
||||
|
|
@ -151,7 +166,7 @@ class ImportExportController extends ControllerBehavior
|
|||
$this->vars['importDbColumns'] = $this->getImportDbColumns();
|
||||
$this->vars['importFileColumns'] = $this->getImportFileColumns();
|
||||
|
||||
// Make these variables to widgets
|
||||
// Make these variables available to widgets
|
||||
$this->controller->vars += $this->vars;
|
||||
}
|
||||
|
||||
|
|
@ -214,17 +229,9 @@ class ImportExportController extends ControllerBehavior
|
|||
|
||||
protected function getImportFilePath()
|
||||
{
|
||||
$model = $this->importGetModel();
|
||||
$file = $model
|
||||
->import_file()
|
||||
->withDeferred($this->importUploadFormWidget->getSessionKey())
|
||||
->first();
|
||||
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $file->getLocalPath();
|
||||
return $this
|
||||
->importGetModel()
|
||||
->getImportFilePath($this->importUploadFormWidget->getSessionKey());
|
||||
}
|
||||
|
||||
public function importIsColumnRequired($columnName)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,12 @@
|
|||
this.processImport = function () {
|
||||
var $form = $('#importFileColumns').closest('form')
|
||||
|
||||
$form.request('onImport')
|
||||
$form.request('onImport', {
|
||||
success: function(data) {
|
||||
$('#importContainer').html(data.result)
|
||||
$(document).trigger('render')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="scoreboard">
|
||||
<div data-control="toolbar">
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4>Created</h4>
|
||||
<p><?= $importResults->created ?></p>
|
||||
</div>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4>Updated</h4>
|
||||
<p><?= $importResults->updated ?></p>
|
||||
</div>
|
||||
<?php if ($importResults->skippedCount): ?>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4>Skipped</h4>
|
||||
<p><?= $importResults->skippedCount ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if ($importResults->warningCount): ?>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4>Warnings</h4>
|
||||
<p><?= $importResults->warningCount ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="scoreboard-item title-value">
|
||||
<h4>Errors</h4>
|
||||
<p><?= $importResults->errorCount ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($importResults->hasMessages): ?>
|
||||
<?php
|
||||
$tabs = [
|
||||
'skipped' => 'Skipped rows',
|
||||
'warnings' => 'Warnings',
|
||||
'errors' => 'Errors',
|
||||
];
|
||||
|
||||
if (!$importResults->skippedCount) unset($tabs['skipped']);
|
||||
if (!$importResults->warningCount) unset($tabs['warnings']);
|
||||
if (!$importResults->errorCount) unset($tabs['errors']);
|
||||
?>
|
||||
<div class="control-tabs secondary-tabs" data-control="tab">
|
||||
<ul class="nav nav-tabs">
|
||||
<?php $count = 0; foreach ($tabs as $code => $tab): ?>
|
||||
<li class="<?= $count++ == 0 ? 'active' : '' ?>">
|
||||
<a href="#importTab<?= $code ?>">
|
||||
<?= $tab ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<?php $count = 0; foreach ($tabs as $code => $tab): ?>
|
||||
<div class="tab-pane <?= $count++ == 0 ? 'active' : '' ?>">
|
||||
<div class="list-preview">
|
||||
<div class="control-simplelist is-divided is-scrollable size-small" data-control="simplelist">
|
||||
<ul>
|
||||
<?php foreach ($importResults->{$code} as $row => $message): ?>
|
||||
<li>
|
||||
<strong>Row <?= $row ?></strong>
|
||||
- <?= e($message) ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.complete')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="modal-body">
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.close')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -187,6 +187,7 @@ return [
|
|||
'close' => 'Close',
|
||||
'confirm' => 'Confirm',
|
||||
'reload' => 'Reload',
|
||||
'complete' => 'Complete',
|
||||
'ok' => 'OK',
|
||||
'or' => 'or',
|
||||
'confirm_tab_close' => 'Do you really want to close the tab? Unsaved changes will be lost.',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php namespace Backend\Models;
|
||||
|
||||
use Model;
|
||||
use League\Csv\Reader as CsvReader;
|
||||
|
||||
/**
|
||||
* Model used for importing data
|
||||
|
|
@ -10,17 +11,152 @@ use Model;
|
|||
*/
|
||||
abstract class ImportModel extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
/**
|
||||
* Relations
|
||||
*/
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Relations
|
||||
*/
|
||||
public $attachOne = [
|
||||
'import_file' => ['System\Models\File']
|
||||
];
|
||||
|
||||
protected $resultStats = [
|
||||
'updated' => 0,
|
||||
'created' => 0,
|
||||
'errors' => [],
|
||||
'warnings' => [],
|
||||
'skipped' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Called when data is being imported.
|
||||
*/
|
||||
abstract public function importData();
|
||||
abstract public function importData($results, $sessionKey = null);
|
||||
|
||||
public function importDataFromColumnMatch($matches, $sessionKey = null, $options = [])
|
||||
{
|
||||
$path = $this->getImportFilePath($sessionKey);
|
||||
$data = $this->processImportData($path, $matches, $options);
|
||||
return $this->importData($data, $sessionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts column index to database column map to an array containing
|
||||
* database column names and values pulled from the CSV file.
|
||||
* Eg:
|
||||
* [0 => [first_name], 1 => [last_name]]
|
||||
*
|
||||
* Will return:
|
||||
* [first_name => Joe, last_name => Blogs],
|
||||
* [first_name => Harry, last_name => Potter],
|
||||
* [...]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function processImportData($filePath, $matches, $options)
|
||||
{
|
||||
extract(array_merge([
|
||||
'firstRowTitles' => true
|
||||
], $options));
|
||||
|
||||
$reader = CsvReader::createFromPath($filePath);
|
||||
|
||||
if ($firstRowTitles) {
|
||||
$reader->setOffset(1);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$contents = $reader->fetchAll();
|
||||
foreach ($contents as $row) {
|
||||
$result[] = $this->processImportRow($row, $matches);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single row of CSV data to the column map.
|
||||
* @return array
|
||||
*/
|
||||
protected function processImportRow($rowData, $matches)
|
||||
{
|
||||
$newRow = [];
|
||||
|
||||
foreach ($matches as $columnIndex => $dbNames) {
|
||||
$value = array_get($rowData, $columnIndex);
|
||||
foreach ((array) $dbNames as $dbName) {
|
||||
$newRow[$dbName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $newRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attached imported file local path, if available.
|
||||
* @return string
|
||||
*/
|
||||
public function getImportFilePath($sessionKey = null)
|
||||
{
|
||||
$file = $this
|
||||
->import_file()
|
||||
->withDeferred($sessionKey)
|
||||
->first();
|
||||
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $file->getLocalPath();
|
||||
}
|
||||
|
||||
//
|
||||
// Result logging
|
||||
//
|
||||
|
||||
public function getResultStats()
|
||||
{
|
||||
$this->resultStats['errorCount'] = count($this->resultStats['errors']);
|
||||
$this->resultStats['warningCount'] = count($this->resultStats['warnings']);
|
||||
$this->resultStats['skippedCount'] = count($this->resultStats['skipped']);
|
||||
|
||||
$this->resultStats['hasMessages'] = (
|
||||
$this->resultStats['errorCount'] > 0 ||
|
||||
$this->resultStats['warningCount'] > 0 ||
|
||||
$this->resultStats['skippedCount'] > 0
|
||||
);
|
||||
|
||||
return (object) $this->resultStats;
|
||||
}
|
||||
|
||||
protected function logUpdated()
|
||||
{
|
||||
$this->resultStats['updated']++;
|
||||
}
|
||||
|
||||
protected function logCreated()
|
||||
{
|
||||
$this->resultStats['created']++;
|
||||
}
|
||||
|
||||
protected function logError($rowIndex, $message)
|
||||
{
|
||||
$this->resultStats['errors'][$rowIndex] = $message;
|
||||
}
|
||||
|
||||
protected function logWarning($rowIndex, $message)
|
||||
{
|
||||
$this->resultStats['warnings'][$rowIndex] = $message;
|
||||
}
|
||||
|
||||
protected function logSkipped($rowIndex, $message)
|
||||
{
|
||||
$this->resultStats['skipped'][$rowIndex] = $message;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue