src/EventListener/OEMUploadListener.php line 26

Open in your IDE?
  1. <?php
  2. namespace App\EventListener;
  3. use Pimcore\Model\Element\ValidationException;
  4. use Pimcore\Event\Model\ElementEventInterface;
  5. use Pimcore\Event\Model\DataObjectEvent;
  6. use Pimcore\Model\DataObject\OEMUpload;
  7. use Pimcore\Log\ApplicationLogger;
  8. use App\Services\BackgroundProcessService;
  9. use PhpOffice\PhpSpreadsheet\IOFactory;
  10. use Pimcore\Model\Asset;
  11. use Pimcore\Model\Asset\Folder;
  12. class OEMUploadListener
  13. {
  14.     public function __construct(private ApplicationLogger $applicationLogger, private BackgroundProcessService $backgroundProcessService)
  15.     {
  16.     }
  17.     /**
  18.      * @param ElementEventInterface $event
  19.      */
  20.     public function onPreAdd(ElementEventInterface $event)
  21.     {
  22.         if ($event instanceof DataObjectEvent) {
  23.             $object $event->getObject();
  24.             if (!$object instanceof OEMUpload) {
  25.                 return;
  26.             }
  27.             $count OEMUpload::getList()->count();
  28.             if ($count >= 1) {
  29.                 throw new ValidationException('We\'re not allowing to create object more than one of this class.');
  30.             }
  31.         }
  32.     }
  33.     /**
  34.      * @param ElementEventInterface $event
  35.      */
  36.     public function onPreUpdate(ElementEventInterface $event)
  37.     {
  38.         if ($event instanceof DataObjectEvent) {
  39.             $object $event->getObject();
  40.             if ($object instanceof OEMUpload) {
  41.                 // Perform mandatory validation
  42.                 $this->mandatoryValidation($object);
  43.             }
  44.         }
  45.     }
  46.     /**
  47.      * @param ElementEventInterface $event
  48.      */
  49.     public function onPostUpdate(ElementEventInterface $event)
  50.     {
  51.         if ($event instanceof DataObjectEvent) {
  52.             $object $event->getObject();
  53.             if (!$object instanceof OEMUpload) {
  54.                 return;
  55.             }
  56.             if ($object->getUploadType() == 'model') {
  57.                 $command 'bulk:model';
  58.             } else {
  59.                 $command 'bulk:oem';
  60.             }
  61.             // Execute command
  62.             $errorLog $command ' command is executed to enrich bulk data by Excel File.';
  63.             $this->backgroundProcessService->execute($command$errorLog);
  64.         }
  65.     }
  66.     /**
  67.      * @param object $object
  68.      */
  69.     public function mandatoryValidation($object)
  70.     {
  71.         $this->validateFile($object);
  72.         $excelFile $object->getOEMFile();
  73.         $filePath $excelFile->getLocalFile();
  74.         $path "OEM Import";
  75.         $pathExists \Pimcore\Model\DataObject\Service::pathExists($path);
  76.         if (empty($pathExists)) {
  77.             $folder \Pimcore\Model\DataObject\Service::createFolderByPath($path);
  78.         } else {
  79.             $folder \Pimcore\Model\DataObject\Service::getElementByPath('object'$path);
  80.         }
  81.         $object->setParentId($folder->getId());
  82.         $object->setOEMFile(null);
  83.         $object->save();
  84.         $expectedColumns $this->getExpectedColumns($object->getUploadType());
  85.         $actualColumns $this->readExcelFile($filePath);
  86.         $this->validateColumns($actualColumns$expectedColumns);
  87.     }
  88.     private function validateFile($object)
  89.     {
  90.         $file $object->getOEMFile();
  91.         if (empty($object->getOEMFile())) {
  92.             throw new ValidationException('Please upload enriched excel file.');
  93.         }
  94.         $type $object->getUploadType();
  95.         if ($type == 'part') {
  96.             $path '/OEM Imports/Part/';
  97.         } else {
  98.             $path '/OEM Imports/Model/';
  99.         }
  100.         $destinationFolder Asset\Folder::getByPath($path);
  101.         if (!$destinationFolder instanceof Asset\Folder) {
  102.             $destinationFolder Asset\Service::createFolderByPath($path);
  103.         }
  104.         $filename basename($file->getFullPath());
  105.         $newFileName rand(15) . $filename;
  106.         $file->setFileName($newFileName);
  107.         $file->setParent($destinationFolder);
  108.         $file->save();
  109.         $excelMimes = [
  110.             'text/xls',
  111.             'text/xlsx',
  112.             'application/excel',
  113.             'application/vnd.msexcel',
  114.             'application/vnd.ms-excel',
  115.             'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  116.         ];
  117.         $file $object->getOEMFile();
  118.         if (!in_array($file->getMimeType(), $excelMimes)) {
  119.             $file->delete();
  120.             throw new ValidationException('Please upload excel file.');
  121.         }
  122.         //if ($file->getFileSize() > 10485760) { // 10MB
  123.         if ($file->getFileSize() > 50 1024 1024) { // 50MB
  124.             $file->delete();
  125.             //throw new ValidationException('Maximum file size should be 10MB.');
  126.             throw new ValidationException('Maximum file size should be 50MB.');
  127.         }
  128.     }
  129.     private function getExpectedColumns($uploadType)
  130.     {
  131.         if ($uploadType === 'model') {
  132.             return [
  133.                 "Variant Barcode(Model ID)",
  134.                 "Store Name",
  135.                 "Shopify ID",
  136.                 "Shopify Status",
  137.                 "Body",
  138.                 "Handle",
  139.                 "Title",
  140.                 "Other Images (If multiple, add | sign to separate values: http://image.com|http://image.com)",
  141.                 "Image Alt Text",
  142.                 "Main Category (If multiple, add | sign to separate values: Mixer|Washdryer)",
  143.                 "Sub Category (If multiple, add | sign to separate values: Mixer|Washdryer)",
  144.                 "Other Category (If multiple, add sign to separate values: Mixer|Washdryer)",
  145.                 "Tags(If multiple, add sign to separate values: Mixer|Washdryer)",
  146.                 "SEO Title",
  147.                 "SEO Description",
  148.                 "Google Shopping Age Group",
  149.                 "Google Shopping Condition",
  150.                 "Google Shopping CustomLabel0",
  151.                 "Google Shopping CustomLabel1",
  152.                 "Google Shopping CustomLabel2",
  153.                 "Google Shopping CustomLabel3",
  154.                 "Google Shopping CustomLabel4",
  155.                 "Google Shopping CustomProduct",
  156.                 "Google Shopping Gender",
  157.                 "Google Shopping Google Product Category",
  158.                 "Google Shopping MPN",
  159.                 "Model SKU(Compatible Parts)(If multiple, add sign to separate values: Mixer|Washdryer)",
  160.                 "Option Name Revision(If multiple, add sign to separate values: Mixer|Washdryer)",
  161.                 "Option Value Revision(If multiple, add sign to separate values: Mixer|Washdryer)",
  162.                 "Product Type",
  163.                 "Right to Repair",
  164.                 "Self Repair Manual",
  165.                 "Service Manual",
  166.                 "Model Specifications",
  167.                 "Model Features",
  168.                 "Specification Manual",
  169.                 "Installation Manual",
  170.                 "Energy Rating Manual",
  171.                 "Ready For Upload"
  172.             ];
  173.         } else {
  174.             return [
  175.                 "Base PN",
  176.                 "Store Name",
  177.                 "Body",
  178.                 "Cost Per Item",
  179.                 "Variant Price",
  180.                 "Variant Compare at Price",
  181.                 "Is Price Update",
  182.                 "Handle",
  183.                 "Title",
  184.                 "Main Image",
  185.                 "Spin Image",
  186.                 "Other Images (If multiple, add comma separated: http://image.com, http://image.com)",
  187.                 "Image Alt Text",
  188.                 "Main Category (If multiple, add comma separated: Mixer, Washdryer)",
  189.                 "Sub Category (If multiple, add comma separated: Mixer, Washdryer)",
  190.                 "Other Category (If multiple, add comma separated: Mixer, Washdryer)",
  191.                 "Web Category",
  192.                 "Tags",
  193.                 "SEO Title",
  194.                 "SEO Description",
  195.                 "Equivalent Part BasePN",
  196.                 "Fbt Part BasePN",
  197.                 "Google Shopping Age Group",
  198.                 "Google Shopping Condition",
  199.                 "Google Shopping CustomLabel0",
  200.                 "Google Shopping CustomLabel1",
  201.                 "Google Shopping CustomLabel2",
  202.                 "Google Shopping CustomLabel3",
  203.                 "Google Shopping CustomLabel4",
  204.                 "Google Shopping CustomProduct",
  205.                 "Google Shopping Gender",
  206.                 "Google Shopping Google Product Category",
  207.                 "Google Shopping MPN",
  208.                 "Compatible Models",
  209.                 "Popular Parts",
  210.                 "Skill Level",
  211.                 "Supercede BasePN",
  212.                 "Variant Barcode",
  213.                 "Variant Fulfillment Service",
  214.                 "Product Type",
  215.                 "Product Quantity",
  216.                 "Variant Inventory Policy",
  217.                 "Variant Inventory Tracker",
  218.                 "No Longer Available",
  219.                 "Ready For Upload",
  220.                 "Shopify ID",
  221.                 "Shopify Status"
  222.             ];
  223.         }
  224.     }
  225.     private function validateColumns(array $actualColumns, array $expectedColumns)
  226.     {
  227.         $filteredActualColumns array_filter($actualColumns, function ($value) {
  228.             return !is_null($value) && $value !== '';
  229.         });
  230.         $normalizedActualColumns array_map('strtolower'$filteredActualColumns);
  231.         $normalizedExpectedColumns array_map('strtolower'$expectedColumns);
  232.         $extraColumns array_diff($normalizedActualColumns$normalizedExpectedColumns);
  233.         $missingColumns array_diff($normalizedExpectedColumns$normalizedActualColumns);
  234.         if (count($filteredActualColumns) !== count($expectedColumns) || !empty($extraColumns) || !empty($missingColumns)) {
  235.             $countMismatchMsg = (count($filteredActualColumns) !== count($expectedColumns))
  236.                 ? "Count of columns does not match. Expected: " count($expectedColumns) . ", Actual: " count($filteredActualColumns)
  237.                 : '';
  238.             $extraColumnsMsg = (!empty($extraColumns) ? ", Extra Column Name: " implode(', '$extraColumns) . ',' '');
  239.             $missingColumnsMsg = (!empty($missingColumns) ? ", Missing Column Name: " implode(', '$missingColumns) : '');
  240.             $logs $countMismatchMsg $extraColumnsMsg $missingColumnsMsg;
  241.             $errorMessage rtrim($logs',');
  242.             throw new ValidationException($errorMessage);
  243.         }
  244.     }
  245.     private function readExcelFile($filePath)
  246.     {
  247.         $spreadsheet IOFactory::load($filePath);
  248.         return $spreadsheet->getActiveSheet()->rangeToArray('A1:' $spreadsheet->getActiveSheet()->getHighestColumn() . '1'nulltruefalse)[0];
  249.     }
  250. }