c
This commit is contained in:
355
inc/Hura8/System/Controller/aExcelUploadController.php
Normal file
355
inc/Hura8/System/Controller/aExcelUploadController.php
Normal file
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
|
||||
namespace Hura8\System\Controller;
|
||||
|
||||
use Hura8\System\ReadExcel;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
|
||||
|
||||
abstract class aExcelUploadController
|
||||
{
|
||||
|
||||
protected $update_option = [];
|
||||
|
||||
protected static $column_names = [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
//...
|
||||
];
|
||||
|
||||
protected $file_input_name = "file_excel"; //
|
||||
|
||||
protected $client_excel_col_config = [
|
||||
/*"A" => array(
|
||||
'name' => 'ID Sản phẩm Web',
|
||||
'width' => '10',
|
||||
'data_field_name' => 'id',
|
||||
),
|
||||
"B" => array(
|
||||
'name' => 'Mã kho (SKU)',
|
||||
'width' => '10',
|
||||
'data_field_name' => 'storeSKU',
|
||||
),*/
|
||||
//...
|
||||
];
|
||||
|
||||
protected $field_column_mappings = [
|
||||
//'name' => "A",
|
||||
//'price' => "B",
|
||||
];
|
||||
|
||||
// hold cache for some operations
|
||||
protected $cache = [];
|
||||
|
||||
protected $format_item_middlewares = []; // list of middleware object to format item
|
||||
|
||||
|
||||
public function __construct($client_config_file_name = '', $file_input_name = '', array $update_option = [])
|
||||
{
|
||||
|
||||
if(!$client_config_file_name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//this from a config file for each client
|
||||
$client_config_file = "config/client/excel/" . $client_config_file_name;
|
||||
if (!file_exists(ROOT_DIR . '/' . $client_config_file)) {
|
||||
die("Please create config file: " . $client_config_file);
|
||||
}
|
||||
$client_fields_config = include ROOT_DIR . '/' . $client_config_file;
|
||||
|
||||
if($file_input_name) {
|
||||
$this->file_input_name = $file_input_name;
|
||||
}
|
||||
|
||||
// auto add excel column names based on fields' index
|
||||
$this->client_excel_col_config = $this->_make_columns(array_values($client_fields_config));
|
||||
|
||||
// create field-col map
|
||||
$field_column_mappings = [];
|
||||
foreach ($this->client_excel_col_config as $column_name => $_prop) {
|
||||
if(!$_prop['data_field_name']) continue;
|
||||
|
||||
// skip column which is not for upload
|
||||
if(isset($_prop['for_upload']) && !$_prop['for_upload']) continue;
|
||||
|
||||
$field_column_mappings[$_prop['data_field_name']] = $column_name;
|
||||
}
|
||||
|
||||
$this->field_column_mappings = $field_column_mappings;
|
||||
|
||||
$this->update_option = $update_option;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function updateUpdateOptions(array $update_option = []) {
|
||||
foreach ($update_option as $key => $value) {
|
||||
$this->update_option[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getColumnNotForUpload() {
|
||||
$result = [];
|
||||
foreach ($this->client_excel_col_config as $column_name => $_prop) {
|
||||
// skip column which is not for upload
|
||||
if(isset($_prop['for_upload']) && !$_prop['for_upload']) $result[$column_name] = $_prop['name'] ;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function getColumnCanUpdate() {
|
||||
$result = [];
|
||||
foreach ($this->client_excel_col_config as $column_name => $_prop) {
|
||||
// skip column which is not for upload
|
||||
if(isset($_prop['for_upload']) && !$_prop['for_upload']) continue;
|
||||
|
||||
// skip column which is not for update
|
||||
if(isset($_prop['can_update']) && !$_prop['can_update']) continue;
|
||||
|
||||
$result[] = $_prop ;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
protected function getRequiredFields() {
|
||||
$result = [];
|
||||
foreach ($this->client_excel_col_config as $column_name => $_prop) {
|
||||
// skip column which is not for upload
|
||||
if(isset($_prop['required']) && $_prop['required']) $result[] = $_prop['data_field_name'] ;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
protected function _make_columns($fields_config)
|
||||
{
|
||||
$new_array = [];
|
||||
$total_names = sizeof(static::$column_names);
|
||||
foreach ($fields_config as $index => $config) {
|
||||
if ($index >= $total_names) break;
|
||||
$new_array[static::$column_names[$index]] = $config;
|
||||
}
|
||||
|
||||
return $new_array;
|
||||
}
|
||||
|
||||
|
||||
protected function getExcelFileExt($excel_file){
|
||||
$ext = substr(strrchr($excel_file, "."), 1);
|
||||
return (in_array($ext, ["xls", "xlsx"])) ? $ext : false;
|
||||
}
|
||||
|
||||
|
||||
public function start($sheet_start_row = 3, $batch_mode = false, $batch_size=100)
|
||||
{
|
||||
$ext = $this->getExcelFileExt($_FILES[$this->file_input_name]["name"]);
|
||||
if(!$ext) {
|
||||
return [
|
||||
'status' => 'error',
|
||||
'message' => 'Invalid excel file',
|
||||
];
|
||||
}
|
||||
|
||||
$objReadExcel = new ReadExcel($ext);
|
||||
|
||||
$all_rows = $objReadExcel->read(
|
||||
$_FILES[$this->file_input_name]["tmp_name"],
|
||||
$sheet_start_row,
|
||||
$this->field_column_mappings,
|
||||
'',
|
||||
true
|
||||
);
|
||||
|
||||
$this->beforeProcessRows($all_rows);
|
||||
|
||||
$success_row_counter = 0;
|
||||
|
||||
if($batch_mode) {
|
||||
// batch mode
|
||||
$small_batch = [];
|
||||
$counter = 0;
|
||||
foreach ($all_rows as $sheet_index => $sheet_content) {
|
||||
foreach ($sheet_content as $row_id => $row_content) {
|
||||
|
||||
$formatted_info = $this->formatItemInfo($this->convertColToField($row_content));
|
||||
if(!$formatted_info) continue;
|
||||
|
||||
$counter += 1;
|
||||
|
||||
$small_batch[] = $formatted_info;
|
||||
if($counter % $batch_size == 0) {
|
||||
$success_row_counter += $this->processBatchItems($small_batch);
|
||||
// reset
|
||||
$counter = 0;
|
||||
$small_batch = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process the remain
|
||||
if(sizeof($small_batch)) {
|
||||
$success_row_counter += $this->processBatchItems($small_batch);
|
||||
}
|
||||
|
||||
} else {
|
||||
// single item mode
|
||||
foreach ($all_rows as $sheet_index => $sheet_content) {
|
||||
foreach ($sheet_content as $row_index => $row_content) {
|
||||
|
||||
$formatted_info = $this->formatItemInfo($this->convertColToField($row_content));
|
||||
if(!$formatted_info) continue;
|
||||
|
||||
if($this->processItem($formatted_info, $row_index)){
|
||||
$success_row_counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//unset($all_rows);
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'message' => '',
|
||||
'success_counter' => $success_row_counter,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
protected function convertExcelDateValue($value) {
|
||||
// Date value in Excel's cell can be displayed as text or date value, we need to check for both
|
||||
$format_value = $this->formatExcelUploadDate($value);
|
||||
|
||||
if(!$format_value) {
|
||||
// check if it's excel date
|
||||
try {
|
||||
$format_value = (is_numeric($value)) ? date("d-m-Y", Date::excelToTimestamp($value, date_default_timezone_get())) : '';
|
||||
}catch (\Exception $e) {
|
||||
$format_value = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $format_value;
|
||||
}
|
||||
|
||||
|
||||
protected function formatExcelUploadDate($input_date) {
|
||||
$check_date_pattern = "/\d{1,2}-\d{1,2}-\d{4}/i";
|
||||
$format_date = str_replace("/", "-", $input_date);
|
||||
|
||||
if(preg_match($check_date_pattern, $format_date)) {
|
||||
return date("d-m-Y", strtotime($format_date));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function convertExcelHourMinuteValue($value) {
|
||||
$format_value = $this->formatExcelUploadHourMinute($value);
|
||||
|
||||
if(!$format_value) {
|
||||
// check if it's excel date
|
||||
try {
|
||||
$format_value = (is_numeric($value)) ? date("H:i", Date::excelToTimestamp($value, date_default_timezone_get())) : '';
|
||||
}catch (\Exception $e) {
|
||||
$format_value = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $format_value;
|
||||
}
|
||||
|
||||
|
||||
protected function formatExcelUploadHourMinute($input_date) {
|
||||
$check_date_pattern = "/\d{1,2}:\d{1,2}/i";
|
||||
//$format_date = str_replace("/", "-", $input_date);
|
||||
|
||||
if(preg_match($check_date_pattern, $input_date)) {
|
||||
return date("H:i", strtotime($input_date));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ['A' => '12', 'B' => 'ten sp'] => ['id' => '12', 'product_name' => 'ten sp']
|
||||
protected function convertColToField(array $row_content ) {
|
||||
|
||||
if(!$this->field_column_mappings) {
|
||||
return array_values($row_content);
|
||||
}
|
||||
|
||||
$item_info = [];
|
||||
foreach ($this->field_column_mappings as $field => $col) {
|
||||
//if(!isset($row_content[$col])) continue;
|
||||
$item_info[$field] = $row_content[$col];
|
||||
}
|
||||
|
||||
return $item_info;
|
||||
}
|
||||
|
||||
// abstract methods
|
||||
protected function beforeProcessRows(array &$all_read_rows){
|
||||
// default nothing
|
||||
// derived class should overwrite this method
|
||||
}
|
||||
abstract protected function processItem(array $item_info, $row_index=0);
|
||||
abstract protected function processBatchItems(array $item_list);
|
||||
|
||||
/**
|
||||
* @param array $item_info
|
||||
* @return array|null
|
||||
*/
|
||||
abstract protected function formatItemInfoBeforeProcess(array $item_info);
|
||||
|
||||
public function registerFormatItemInfoMiddleware($middleware_ojb)
|
||||
{
|
||||
$this->format_item_middlewares[] = $middleware_ojb;
|
||||
}
|
||||
|
||||
|
||||
protected $date_fields = [];
|
||||
public function setDateFields(array $date_fields) {
|
||||
$this->date_fields = $date_fields;
|
||||
}
|
||||
|
||||
protected $hour_minute_fields = [];
|
||||
public function setHourMinuteFields(array $hour_minute_fields) {
|
||||
$this->hour_minute_fields = $hour_minute_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $item_info
|
||||
* @return array|null
|
||||
*/
|
||||
protected function formatItemInfo(array $item_info)
|
||||
{
|
||||
$copy = $item_info;
|
||||
|
||||
// apply middleware
|
||||
if (sizeof($this->format_item_middlewares)) {
|
||||
foreach ($this->format_item_middlewares as $_middleware) {
|
||||
$copy = call_user_func($_middleware, $copy);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->date_fields as $field) {
|
||||
$copy[$field] = $this->convertExcelDateValue($item_info[$field]);
|
||||
}
|
||||
|
||||
foreach ($this->hour_minute_fields as $field) {
|
||||
$copy[$field] = $this->convertExcelHourMinuteValue($item_info[$field]);
|
||||
}
|
||||
|
||||
return $this->formatItemInfoBeforeProcess($copy);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user