feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
292
modules/backend/models/ImportModel.php
Normal file
292
modules/backend/models/ImportModel.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php namespace Backend\Models;
|
||||
|
||||
use Backend\Behaviors\ImportExportController\TranscodeFilter;
|
||||
use Str;
|
||||
use Lang;
|
||||
use Model;
|
||||
use League\Csv\Reader as CsvReader;
|
||||
use League\Csv\Statement as CsvStatement;
|
||||
|
||||
/**
|
||||
* Model used for importing data
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
abstract class ImportModel extends Model
|
||||
{
|
||||
use \Winter\Storm\Database\Traits\Validation;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Relations
|
||||
*/
|
||||
public $attachOne = [
|
||||
'import_file' => [\System\Models\File::class, 'public' => false],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Import statistics store.
|
||||
*/
|
||||
protected $resultStats = [
|
||||
'updated' => 0,
|
||||
'created' => 0,
|
||||
'errors' => [],
|
||||
'warnings' => [],
|
||||
'skipped' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Called when data is being imported.
|
||||
* The $results array should be in the format of:
|
||||
*
|
||||
* [
|
||||
* 'db_name1' => 'Some value',
|
||||
* 'db_name2' => 'Another value'
|
||||
* ],
|
||||
* [...]
|
||||
*
|
||||
*/
|
||||
abstract public function importData($results, $sessionKey = null);
|
||||
|
||||
/**
|
||||
* Import data based on column names matching header indexes in the CSV.
|
||||
* The $matches array should be in the format of:
|
||||
*
|
||||
* [
|
||||
* 0 => [db_name1, db_name2],
|
||||
* 1 => [db_name3],
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* The key (0, 1) is the column index in the CSV and the value
|
||||
* is another array of target database column names.
|
||||
*/
|
||||
public function import($matches, $options = [])
|
||||
{
|
||||
$sessionKey = array_get($options, 'sessionKey');
|
||||
$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)
|
||||
{
|
||||
/*
|
||||
* Parse options
|
||||
*/
|
||||
$defaultOptions = [
|
||||
'firstRowTitles' => true,
|
||||
'delimiter' => null,
|
||||
'enclosure' => null,
|
||||
'escape' => null,
|
||||
'encoding' => null
|
||||
];
|
||||
|
||||
$options = array_merge($defaultOptions, $options);
|
||||
|
||||
/*
|
||||
* Read CSV
|
||||
*/
|
||||
$reader = CsvReader::createFromPath($filePath, 'r');
|
||||
|
||||
if ($options['delimiter'] !== null) {
|
||||
$reader->setDelimiter($options['delimiter']);
|
||||
}
|
||||
|
||||
if ($options['enclosure'] !== null) {
|
||||
$reader->setEnclosure($options['enclosure']);
|
||||
}
|
||||
|
||||
if ($options['escape'] !== null) {
|
||||
$reader->setEscape($options['escape']);
|
||||
}
|
||||
|
||||
if (
|
||||
$options['encoding'] !== null &&
|
||||
$reader->supportsStreamFilter()
|
||||
) {
|
||||
$reader->addStreamFilter(sprintf(
|
||||
'%s%s:%s',
|
||||
TranscodeFilter::FILTER_NAME,
|
||||
strtolower($options['encoding']),
|
||||
'utf-8'
|
||||
));
|
||||
}
|
||||
|
||||
// Create reader statement
|
||||
$stmt = (new CsvStatement)
|
||||
->where(function (array $row) {
|
||||
// Filter out empty rows
|
||||
return count($row) > 1 || reset($row) !== null;
|
||||
});
|
||||
|
||||
if ($options['firstRowTitles']) {
|
||||
$stmt = $stmt->offset(1);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$contents = $stmt->process($reader);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explodes a string using pipes (|) to a single dimension array
|
||||
* @return array
|
||||
*/
|
||||
protected function decodeArrayValue($value, $delimeter = '|')
|
||||
{
|
||||
if (strpos($value, $delimeter) === false) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
$data = preg_split('~(?<!\\\)' . preg_quote($delimeter, '~') . '~', $value);
|
||||
$newData = [];
|
||||
|
||||
foreach ($data as $_value) {
|
||||
$newData[] = str_replace('\\'.$delimeter, $delimeter, $_value);
|
||||
}
|
||||
|
||||
return $newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attached imported file local path, if available.
|
||||
* @return string
|
||||
*/
|
||||
public function getImportFilePath($sessionKey = null)
|
||||
{
|
||||
$file = $this
|
||||
->import_file()
|
||||
->withDeferred($sessionKey)
|
||||
->orderBy('id', 'desc')
|
||||
->first()
|
||||
;
|
||||
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $file->getLocalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available encodings values from the localization config
|
||||
* @return array
|
||||
*/
|
||||
public function getFormatEncodingOptions()
|
||||
{
|
||||
$options = [
|
||||
'utf-8',
|
||||
'us-ascii',
|
||||
'iso-8859-1',
|
||||
'iso-8859-2',
|
||||
'iso-8859-3',
|
||||
'iso-8859-4',
|
||||
'iso-8859-5',
|
||||
'iso-8859-6',
|
||||
'iso-8859-7',
|
||||
'iso-8859-8',
|
||||
'iso-8859-9',
|
||||
'iso-8859-10',
|
||||
'iso-8859-11',
|
||||
'iso-8859-13',
|
||||
'iso-8859-14',
|
||||
'iso-8859-15',
|
||||
'Windows-1250',
|
||||
'Windows-1251',
|
||||
'Windows-1252'
|
||||
];
|
||||
|
||||
$translated = array_map(function ($option) {
|
||||
return Lang::get('backend::lang.import_export.encodings.'.Str::slug($option, '_'));
|
||||
}, $options);
|
||||
|
||||
return array_combine($options, $translated);
|
||||
}
|
||||
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user