Current File : /var/www/vinorea/modules/ipexportimport/classes/export/EIAExport.php
<?php
/**
 *
 * NOTICE OF LICENSE
 *
 *  @author    SmartPresta <tehran.alishov@gmail.com>
 *  @copyright 2024 SmartPresta
 *  @license   http://opensource.org/licenses/afl-3.0.php Commercial License!
 */

if (!defined('_PS_VERSION_')) {
    exit;
}

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

abstract class EIAExport
{
    protected $entityNamePl;
    protected $auto;
    protected $file;
    protected $data;
    protected $module;
    protected $context;
    protected $exportDir;
    protected $encl;
    protected $dlm;
    protected $inputs;
    protected $fileType;
    protected $selectedColumns;
    protected $datatables;
    protected $offset;
    protected $limit;
    protected $langId;
    protected $shopId;
    protected $sort;
    protected $sortWay;
    protected $sql;
    protected $fracPart;
    public $errors;
    protected $multivalueSeparator;
    
    public function __construct($module)
    {
        $this->module = $module;
        $this->context = Context::getContext();
        $this->exportDir = dirname(__FILE__) . '/../../export/';
    }
    
    abstract protected function getDataFromDb();
    
    public function run($auto = null)
    {
        $this->auto = $auto;
        $this->encl = '"';
        
        if ($auto) {
            $this->inputs = $auto['config']['inputs'];
            $this->fracPart = (int) $this->inputs['decimals'] == -1 ? 6 : (int) $this->inputs['decimals'];

            $this->fileType = $this->inputs['as'];
            $docName = $this->inputs['doc_name'] ?: $this->entityNamePl;

            $this->selectedColumns = $this->module->convertToUsableColumns($auto['config']['columns']);
            $this->langId = (int) $this->inputs['language'];
            $this->shopId = (int) $this->inputs['shop'] ?: (int) Configuration::get('PS_SHOP_DEFAULT');

            if ($this->fileType === 'csv') {
                $this->dlm = $this->inputs['csv_separator'];
                if ($this->dlm === 'other') {
                    $this->dlm = $this->inputs['custom_separator'];
                }
//                $this->encl = $this->inputs['csv_enclosure'];
                if ($this->dlm === 't') {
                    $this->dlm = "\t";
                }
            }

            $this->datatables = json_decode($auto['datatables'], true);
            
            $fileId = $auto['fileId'];
            $this->file = $this->exportDir . $fileId . '.' . $this->fileType;
            
            $this->sort = pSQL($this->inputs['sort']);
            $this->sortWay = (int) $this->inputs['sort_way'] === 0 ? ' DESC' : ' ASC';
            
            $this->multivalueSeparator = pSQL($this->inputs['multivalue_separator']) ?: ',';

            $this->offset = $auto['offset'];
            $this->limit = $auto['limit'];

            if (!$this->data && Configuration::getGlobalValue('IPEA_SCHDL_DNSEM')) {
                return;
            }
        } else {
            $this->fileType = Tools::getValue('as');
            $docName = Tools::getValue('doc_name') ?: $this->entityNamePl;
            $this->fracPart = (int) Tools::getValue('decimals');
            $this->fracPart  = $this->fracPart == -1 ? 6 : $this->fracPart;

            $this->selectedColumns = json_decode(Tools::getValue('selectedColumns'), true);
            $this->langId = (int) Tools::getValue('language');
            $this->shopId = (int) Tools::getValue('shop') ?: (int) Configuration::get('PS_SHOP_DEFAULT');

            if ($this->fileType === 'csv') {
                $this->dlm = Tools::getValue('csv_separator');
                if ($this->dlm === 'other') {
                    $this->dlm = Tools::getValue('custom_separator');
                }
//                $this->encl = Tools::getValue('csv_enclosure');
                if ($this->dlm === 't') {
                    $this->dlm = "\t";
                }
            }

            $fileId = Tools::getValue('fileId') ?: mt_rand() . uniqid();
            
            $this->sort = pSQL(Tools::getValue('sort'));
            $this->sortWay = (int) Tools::getValue('sort_way') === 0 ? ' DESC' : ' ASC';
            
            $this->multivalueSeparator = pSQL(Tools::getValue('multivalue_separator')) ?: ',';

            $this->offset = (int) Tools::getValue('offset');
            $this->limit = (int) Tools::getValue('limit');
        }

//        if ($this->encl === 'none') {
//            $this->encl = '';
//        } elseif ($this->encl === 'quot') {
//            $this->encl = '"';
//        }

        $this->file = $this->exportDir . $fileId . '.' . $this->fileType;
        
        $results = ['fileId' => $fileId];
        
        $doneCount = 0;
        
        $this->data = $this->getDataFromDb();
        if ($this->offset === 0) {
            $total = $this->getTotalDataFromDb();
            $results['totalCount'] = (int) $total;
        }
        
        if ($this->fileType === 'csv') {
            $this->writeToCsv();
        } elseif ($this->fileType === 'xlsx') {
            $this->writeToExcel();
        } elseif ($this->fileType === 'xml') {
            $this->writeToXml();
        } elseif ($this->fileType === 'json') {
            $this->writeToJson();
        } else {
            throw new PrestaShopException('Invalid file type.');
        }
        $doneCount += $this->data ? count($this->data) : 0;

        $results['isFinished'] = $doneCount < $this->limit;
        $results['doneCount'] = $this->offset + $doneCount;
        
        if (!$results['isFinished']) {
            // Since we'll have to POST this array from ajax for the next call, we should care about it size.
            $results['nextPostSize'] = 1024 * 64; // 64KB more for the rest of the POST query.
            $results['postSizeLimit'] = Tools::getMaxUploadSize();
        }

        // Retrieve errors/warnings if any
        if (isset($this->errors) && count($this->errors) > 0) {
            $results['errors'] = $this->errors;
        }
        if (isset($this->warnings) && count($this->warnings) > 0) {
            $results['warnings'] = $this->warnings;
        }
        if (isset($this->informations) && count($this->informations) > 0) {
            $results['informations'] = $this->informations;
        }

        if ($results['isFinished']) {
            $results['type'] = $this->fileType;
            $results['name'] = $docName;
        }
        die(json_encode($results));
    }
    
    protected function getTotalDataFromDb()
    {
        return Db::getInstance()->getValue('SELECT FOUND_ROWS()');
    }

    protected function writeToCsv()
    {
        $handle = $this->openCsvFile();
        foreach ($this->data as $value) {
            fputcsv($handle, $value, $this->dlm, $this->encl);
        }
        $this->closeCsvFile($handle);
    }
    
    protected function openCsvFile()
    {
        $handle = false;
        if (file_exists($this->file)) {
            $handle = fopen($this->file, 'a');
        } else {
            $handle = fopen($this->file, 'w');
            fwrite($handle, "\xEF\xBB\xBF"); // NEW LINE
            if ($handle) {
                if ($this->data) {
                    fputcsv($handle, array_keys($this->data[0]), $this->dlm, $this->encl);
                } else {
                    fputcsv($handle, [$this->module->l('No Data', 'EIAAddressesExport')], $this->dlm, $this->encl);
                }
            }
        }

        if (!$handle) {
            $this->errors[] = Tools::displayError('Cannot read the file');
        }

        return $handle;
    }

    protected function closeCsvFile($handle)
    {
        fclose($handle);
    }
        
    protected function writeToJson()
    {
        if (file_exists($this->file)) {
            $data = json_decode(Tools::file_get_contents($this->file), true);
        } else {
            $data = [];
        }
        $data = array_merge($data, $this->data);
        file_put_contents($this->file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }
    
    protected function writeToXml()
    {
        $this->makeXmlHierarchy();
        if (file_exists($this->file)) {
            $data = $this->arrayToXml(array_merge(array('arrType' => $this->xmlMainTag), $this->data), null, simplexml_load_file($this->file));
        } else {
            $data = $this->arrayToXml(array_merge(array('arrType' => $this->xmlMainTag), $this->data), '<?xml version="1.0" encoding="UTF-8"?><' . $this->xmlMainTagPl . ' />');
        }
//        ddd($data);
        $domxml = new DOMDocument('1.0');
        $domxml->preserveWhiteSpace = false;
        $domxml->formatOutput = true;
        /* @var $xml SimpleXMLElement */
        $domxml->loadXML($data);

        $domxml->save($this->file, LIBXML_NOEMPTYTAG);
    }
    
    public function writeToXmlWithWriter()
    {
        $writer = new XMLWriter();
        $writer->openURI($this->file);
//        $writer->setIndent(true);
        $writer->startDocument("1.0");
        $writer->startElement("greeting");
        $writer->text('Hello World');
        $writer->endDocument();
        $writer->flush();
    }

    public function makeXmlHierarchy()
    {
        
    }
    
    public function arrayToXml($array, $rootElement = null, $xml = null)
    {
        $_xml = $xml;

        // If there is no Root Element then insert root
        if ($_xml === null) {
            $_xml = new SimpleXMLElement($rootElement !== null ? $rootElement : '<root />');
        }

        // Visit all key value pairs
        foreach ($array as $k => $v) {
            $k = preg_replace('/\(|\)|\.|\s|\'/', '', $k);
            // If there is nested array then
            if (is_array($v)) {
                if (is_numeric($k)) {
                    $k = $array['arrType']; //dealing with <0/>..<n/> issues
                }
                // Call function for nested array
                $this->arrayToXml($v, $k, $_xml->addChild($k));
            } else {
                if ($k === 'arrType') {
                    continue;
                }
                // Simply add child element.
                $_xml->addChild($k, htmlspecialchars($v));
            }
        }

        return $_xml->asXML();
    }
    
    protected function writeToExcel()
    {
        require_once dirname(__FILE__) . '/../../vendor/autoload.php';

        if (file_exists($this->file)) {
            $spreadsheet = IOFactory::load($this->file);
            $sheet = $spreadsheet->getActiveSheet();
            $excelColumns = EIAHelper::createColumnsArray(count($this->data[0]));
        } else {
            $spreadsheet = new Spreadsheet();
            $sheet = $spreadsheet->getActiveSheet();

            // Set document properties
            $spreadsheet->getProperties()->setCreator('Tehran Alishov')
                ->setLastModifiedBy('Tehran Alishov')
                ->setTitle('Office 2007 XLSX Addresses Document')
                ->setSubject('Office 2007 XLSX Addresses Document')
                ->setDescription('Addresses document for Office 2007 XLSX, generated using PHP classes.')
                ->setKeywords('office 2007 openxml php')
                ->setCategory('Addresses result file');
            $spreadsheet->setActiveSheetIndex(0);

            if ($this->data) {
                $excelColumns = EIAHelper::createColumnsArray(count($this->data[0]));
                $sheet->getDefaultColumnDimension()->setWidth(21);
                $sheet->getDefaultRowDimension()->setRowHeight(30);

                $spreadsheet->getDefaultStyle()->getAlignment()
                    ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)
                    ->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);

                $sheet->getStyle('A1:' . end($excelColumns) . (count($this->data)))
                    ->getAlignment()->setWrapText(true);

                $sheet->getStyle('A1:' . end($excelColumns) . '1')
                    ->getFont()->setBold(true);
                $sheet->getStyle('A1:' . end($excelColumns) . '1')
                    ->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
                    ->getStartColor()->setARGB('FFDCF0FF');
                $sheet->getStyle('A1:' . end($excelColumns) . '1')->getBorders()
                    ->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);

                // Rename worksheet
                $sheet->setTitle($this->entityNamePl);
                
                $sheet->fromArray(array_keys($this->data[0]), null, 'A1');
            } else {
                $sheet->setCellValue('A1', $this->module->l('No Data', 'EIAExport'));
            }
        }
        
        $sheet->fromArray($this->data, null, 'A' . ($this->offset + 2));

        $sheet->setSelectedCell('A1');

        // Write to file
        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save($this->file);
    }

    protected function writeToExcelWithBoxSpout()
    {
//        require_once dirname(__FILE__) . '/../../vendor/autoload.php';
        
//        $writer = WriterFactory::create(Type::XLSX); // for XLSX files
        //$writer = WriterFactory::create(Type::CSV); // for CSV files
        //$writer = WriterFactory::create(Type::ODS); // for ODS files
        
//        $style = (new StyleBuilder())
//                ->setFontBold()
//                ->setFontColor(Color::BLUE)
//                ->setShouldWrapText()
////                ->setCellAlignment(CellAlignment::RIGHT) // Does not exist in this library version
//                ->setBackgroundColor(Color::YELLOW)
//                ->build();
        
//        if (file_exists($this->file)) {
//            $info = pathinfo($this->file);
//            $fileRead = $info['dirname'] . '/' . $info['filename'] . '_.xlsx';
//            rename($this->file, $fileRead);
//            $reader = ReaderFactory::create(Type::XLSX); //for XLSX files
//            $reader->open($fileRead);
//
//            $writer->openToFile($this->file);
//            foreach ($reader->getSheetIterator() as $sheet) {
//                foreach ($sheet->getRowIterator() as $row) {
//                    $writer->addRow($row);
//                }
//            }
//            $reader->close();
//            $writer->addRows($this->data);
//        } else {
//            $writer->openToFile($this->file); // write data to a file or to a PHP stream
//            if (!$this->data) {
//                $writer->addRow([$this->module->l('No Data', 'EIAAddressesExport')]);
//            } else {
//                $writer->addRows(array_merge([array_keys($this->data[0])], $this->data)); // add multiple rows at a time
//            }
//        }
//
//        $writer->close();
    }
    
    protected function getImageType($type)
    {
        if (method_exists('ImageType', 'getFormatedName')) {
            $iType = ImageType::getFormatedName($type);
        } else {
            $iType = ImageType::getFormattedName($type);
        }
        return $iType;
    }
}