vendor/w-vision/data-definitions/src/DataDefinitionsBundle/Importer/Importer.php line 235

Open in your IDE?
  1. <?php
  2. /**
  3.  * Data Definitions.
  4.  *
  5.  * LICENSE
  6.  *
  7.  * This source file is subject to the GNU General Public License version 3 (GPLv3)
  8.  * For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
  9.  * files that are distributed with this source code.
  10.  *
  11.  * @copyright  Copyright (c) 2016-2019 w-vision AG (https://www.w-vision.ch)
  12.  * @license    https://github.com/w-vision/DataDefinitions/blob/master/gpl-3.0.txt GNU General Public License version 3 (GPLv3)
  13.  */
  14. namespace Wvision\Bundle\DataDefinitionsBundle\Importer;
  15. use CoreShop\Component\Registry\ServiceRegistryInterface;
  16. use Pimcore\File;
  17. use Pimcore\Mail;
  18. use Pimcore\Model\DataObject\ClassDefinition;
  19. use Pimcore\Model\DataObject\Concrete;
  20. use Pimcore\Model\DataObject\Service;
  21. use Pimcore\Model\Document;
  22. use Pimcore\Model\Factory;
  23. use Pimcore\Model\Version;
  24. use Pimcore\Placeholder;
  25. use Psr\Log\LoggerAwareInterface;
  26. use Psr\Log\LoggerInterface;
  27. use Wvision\Bundle\DataDefinitionsBundle\Event\EventDispatcherInterface;
  28. use Wvision\Bundle\DataDefinitionsBundle\Exception\DoNotSetException;
  29. use Wvision\Bundle\DataDefinitionsBundle\Exception\UnexpectedValueException;
  30. use Wvision\Bundle\DataDefinitionsBundle\Filter\FilterInterface;
  31. use Wvision\Bundle\DataDefinitionsBundle\Loader\LoaderInterface;
  32. use Wvision\Bundle\DataDefinitionsBundle\Model\DataSetAwareInterface;
  33. use Wvision\Bundle\DataDefinitionsBundle\Model\ImportDefinitionInterface;
  34. use Wvision\Bundle\DataDefinitionsBundle\Model\ImportMapping;
  35. use Wvision\Bundle\DataDefinitionsBundle\Model\ParamsAwareInterface;
  36. use Wvision\Bundle\DataDefinitionsBundle\Provider\ImportDataSetInterface;
  37. use Wvision\Bundle\DataDefinitionsBundle\Provider\ImportProviderInterface;
  38. use Wvision\Bundle\DataDefinitionsBundle\Runner\RunnerInterface;
  39. use Wvision\Bundle\DataDefinitionsBundle\Runner\SaveRunnerInterface;
  40. use Wvision\Bundle\DataDefinitionsBundle\Runner\SetterRunnerInterface;
  41. use Wvision\Bundle\DataDefinitionsBundle\Setter\SetterInterface;
  42. final class Importer implements ImporterInterface
  43. {
  44.     /**
  45.      * @var ServiceRegistryInterface
  46.      */
  47.     private $providerRegistry;
  48.     /**
  49.      * @var ServiceRegistryInterface
  50.      */
  51.     private $filterRegistry;
  52.     /**
  53.      * @var ServiceRegistryInterface
  54.      */
  55.     private $runnerRegistry;
  56.     /**
  57.      * @var ServiceRegistryInterface
  58.      */
  59.     private $interpreterRegistry;
  60.     /**
  61.      * @var ServiceRegistryInterface
  62.      */
  63.     private $setterRegistry;
  64.     /**
  65.      * @var ServiceRegistryInterface
  66.      */
  67.     private $cleanerRegistry;
  68.     /**
  69.      * @var ServiceRegistryInterface
  70.      */
  71.     private $loaderRegistry;
  72.     /**
  73.      * @var EventDispatcherInterface
  74.      */
  75.     private $eventDispatcher;
  76.     /**
  77.      * @var LoggerInterface
  78.      */
  79.     private $logger;
  80.     /** @var Factory */
  81.     private $modelFactory;
  82.     /**
  83.      * @var bool
  84.      */
  85.     private $shouldStop false;
  86.     /**
  87.      * Importer constructor.
  88.      * @param ServiceRegistryInterface $providerRegistry
  89.      * @param ServiceRegistryInterface $filterRegistry
  90.      * @param ServiceRegistryInterface $runnerRegistry
  91.      * @param ServiceRegistryInterface $interpreterRegistry
  92.      * @param ServiceRegistryInterface $setterRegistry
  93.      * @param ServiceRegistryInterface $cleanerRegistry
  94.      * @param ServiceRegistryInterface $loaderRegistry
  95.      * @param EventDispatcherInterface $eventDispatcher
  96.      * @param LoggerInterface $logger
  97.      * @param Factory $modelFactory
  98.      */
  99.     public function __construct(
  100.         ServiceRegistryInterface $providerRegistry,
  101.         ServiceRegistryInterface $filterRegistry,
  102.         ServiceRegistryInterface $runnerRegistry,
  103.         ServiceRegistryInterface $interpreterRegistry,
  104.         ServiceRegistryInterface $setterRegistry,
  105.         ServiceRegistryInterface $cleanerRegistry,
  106.         ServiceRegistryInterface $loaderRegistry,
  107.         EventDispatcherInterface $eventDispatcher,
  108.         LoggerInterface $logger,
  109.         Factory $modelFactory
  110.     ) {
  111.         $this->providerRegistry $providerRegistry;
  112.         $this->filterRegistry $filterRegistry;
  113.         $this->runnerRegistry $runnerRegistry;
  114.         $this->interpreterRegistry $interpreterRegistry;
  115.         $this->setterRegistry $setterRegistry;
  116.         $this->cleanerRegistry $cleanerRegistry;
  117.         $this->loaderRegistry $loaderRegistry;
  118.         $this->eventDispatcher $eventDispatcher;
  119.         $this->logger $logger;
  120.         $this->modelFactory $modelFactory;
  121.     }
  122.     /**
  123.      * {@inheritdoc}
  124.      */
  125.     public function doImport(ImportDefinitionInterface $definition$params)
  126.     {
  127.         $filter null;
  128.         $objectIds = [];
  129.         $exceptions = [];
  130.         if ($definition->getCreateVersion()) {
  131.             Version::enable();
  132.         } else {
  133.             Version::disable();
  134.         }
  135.         $filterType $definition->getFilter();
  136.         if ($filterType) {
  137.             $filter $this->filterRegistry->get($filterType);
  138.         }
  139.         /** @var ImportDataSetInterface|array $data */
  140.         $data $this->getData($definition$params);
  141.         if ((is_array($data) || $data instanceof \Countable) && \count($data) > 0) {
  142.             $this->eventDispatcher->dispatch($definition'data_definitions.import.total', \count($data), $params);
  143.         }
  144.         list($objectIds$exceptions) = $this->runImport($definition$params$filter$data);
  145.         $cleanerType $definition->getCleaner();
  146.         if ($cleanerType) {
  147.             $cleaner $this->cleanerRegistry->get($cleanerType);
  148.             $this->logger->info(sprintf('Running Cleaner "%s"'$cleanerType));
  149.             $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  150.                 sprintf('Running Cleaner "%s"'$cleanerType));
  151.             if ($cleaner instanceof DataSetAwareInterface) {
  152.                 $cleaner->setDataSet($data);
  153.             }
  154.             if ($cleaner instanceof ParamsAwareInterface) {
  155.                 $cleaner->setParams($params);
  156.             }
  157.             if ($cleaner instanceof LoggerAwareInterface) {
  158.                 $cleaner->setLogger($this->logger);
  159.             }
  160.             $cleaner->cleanup($definition$objectIds);
  161.             $this->logger->info(sprintf('Finished Cleaner "%s"'$cleanerType));
  162.             $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  163.                 sprintf('Finished Cleaner "%s"'$cleanerType));
  164.         }
  165.         if (\count($exceptions) > 0) {
  166.             $this->processFailedImport($definition$params$objectIds$exceptions);
  167.         } else {
  168.             $this->processSuccessfullImport($definition$params$objectIds$exceptions);
  169.         }
  170.         $this->eventDispatcher->dispatch($definition'data_definitions.import.finished'''$params);
  171.         return $objectIds;
  172.     }
  173.     /**
  174.      * @param ImportDefinitionInterface $definition
  175.      * @param                           $params
  176.      * @param                           $objectIds
  177.      * @param                           $exceptions
  178.      * @throws \Exception
  179.      */
  180.     public function processSuccessfullImport(ImportDefinitionInterface $definition$params$objectIds$exceptions)
  181.     {
  182.         $this->sendDocument($definitionDocument::getById($definition->getSuccessNotificationDocument()),
  183.             $objectIds$exceptions);
  184.         $this->eventDispatcher->dispatch($definition'data_definitions.import.success'$params);
  185.     }
  186.     /**
  187.      * @param ImportDefinitionInterface $definition
  188.      * @param                           $params
  189.      * @param                           $objectIds
  190.      * @param                           $exceptions
  191.      * @throws \Exception
  192.      */
  193.     public function processFailedImport(ImportDefinitionInterface $definition$params$objectIds$exceptions)
  194.     {
  195.         $this->sendDocument($definitionDocument::getById($definition->getFailureNotificationDocument()),
  196.             $objectIds$exceptions);
  197.         $this->eventDispatcher->dispatch($definition'data_definitions.import.failure'$params);
  198.     }
  199.     /**
  200.      * @return void
  201.      */
  202.     public function stop() : void
  203.     {
  204.         $this->shouldStop true;
  205.     }
  206.     /**
  207.      * @param ImportDefinitionInterface $definition
  208.      * @param                           $document
  209.      * @param array                     $objectIds
  210.      * @param array                     $exceptions
  211.      * @throws \Exception
  212.      */
  213.     private function sendDocument(ImportDefinitionInterface $definition$document$objectIds$exceptions)
  214.     {
  215.         if ($document instanceof Document) {
  216.             $params = [
  217.                 'exceptions' => $exceptions,
  218.                 'objectIds' => $objectIds,
  219.                 'className' => $definition->getClass(),
  220.                 'countObjects' => \count($objectIds),
  221.                 'countExceptions' => \count($exceptions),
  222.                 'name' => $definition->getName(),
  223.                 'provider' => $definition->getProvider(),
  224.             ];
  225.             if ($document instanceof Document\Email) {
  226.                 $mail = new Mail();
  227.                 $mail->setDocument($document);
  228.                 $mail->setParams($params);
  229.                 $mail->send();
  230.             } elseif (is_a($document"\\Pimcore\\Model\\Document\\Pushover")) {
  231.                 $document->send($params);
  232.             }
  233.         }
  234.     }
  235.     /**
  236.      * @param ImportDefinitionInterface $definition
  237.      * @param                           $params
  238.      * @return ImportDataSetInterface|array
  239.      */
  240.     private function getData(ImportDefinitionInterface $definition$params)
  241.     {
  242.         /** @var ImportProviderInterface $provider */
  243.         $provider $this->providerRegistry->get($definition->getProvider());
  244.         return $provider->getData($definition->getConfiguration(), $definition$params);
  245.     }
  246.     /**
  247.      * @param ImportDefinitionInterface    $definition
  248.      * @param                              $params
  249.      * @param null                         $filter
  250.      * @param ImportDataSetInterface|array $dataSet
  251.      * @return array
  252.      * @throws \Exception
  253.      */
  254.     private function runImport(ImportDefinitionInterface $definition$params$filter null$dataSet null)
  255.     {
  256.         if (null === $dataSet) {
  257.             $dataSet = [];
  258.         }
  259.         $count 0;
  260.         $countToClean 1000;
  261.         $objectIds = [];
  262.         $exceptions = [];
  263.         foreach ($dataSet as $row) {
  264.             try {
  265.                 $object $this->importRow($definition$row$dataSetarray_merge($params, ['row' => $count]), $filter);
  266.                 if ($object instanceof Concrete) {
  267.                     $objectIds[] = $object->getId();
  268.                 }
  269.                 if (($count 1) % $countToClean === 0) {
  270.                     \Pimcore::collectGarbage();
  271.                     $this->logger->info('Clean Garbage');
  272.                     $this->eventDispatcher->dispatch($definition'data_definitions.import.status''Collect Garbage',
  273.                         $params);
  274.                 }
  275.                 $count++;
  276.             } catch (\Throwable $ex) {
  277.                 $this->logger->error($ex);
  278.                 $exceptions[] = $ex;
  279.                 $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  280.                     sprintf('Error: %s'$ex->getMessage()), $params);
  281.                 if ($definition->getStopOnException()) {
  282.                     throw $ex;
  283.                 }
  284.             }
  285.             $this->eventDispatcher->dispatch($definition'data_definitions.import.progress'''$params);
  286.             if ($this->shouldStop) {
  287.                 $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  288.                     'Process has been stopped.');
  289.                 return [$objectIds$exceptions];
  290.             }
  291.         }
  292.         return [$objectIds$exceptions];
  293.     }
  294.     /**
  295.      * @param ImportDefinitionInterface    $definition
  296.      * @param array                        $data
  297.      * @param ImportDataSetInterface|array $dataSet
  298.      * @param                              $params
  299.      * @param null                         $filter
  300.      * @return null|Concrete
  301.      * @throws \Exception
  302.      */
  303.     private function importRow(ImportDefinitionInterface $definition$data$dataSet$params$filter null)
  304.     {
  305.         if (null === $data) {
  306.             return null;
  307.         }
  308.         $runner null;
  309.         $object $this->getObject($definition$data$params);
  310.         if (null !== $object && !$object->getId()) {
  311.             if ($definition->getSkipNewObjects()) {
  312.                 $this->eventDispatcher->dispatch($definition'data_definitions.import.status''Ignoring new Object',
  313.                     $params);
  314.                 return null;
  315.             }
  316.         } else {
  317.             if ($definition->getSkipExistingObjects()) {
  318.                 $this->eventDispatcher->dispatch($definition'data_definitions.import.status''Ignoring existing Object',
  319.                     $params);
  320.                 return null;
  321.             }
  322.         }
  323.         if ($filter instanceof FilterInterface) {
  324.             if ($filter instanceof DataSetAwareInterface) {
  325.                 $filter->setDataSet($dataSet);
  326.             }
  327.             if ($filter instanceof LoggerAwareInterface) {
  328.                 $filter->setLogger($this->logger);
  329.             }
  330.             if (!$filter->filter($definition$data$object)) {
  331.                 $this->eventDispatcher->dispatch($definition'data_definitions.import.status''Filtered Object'$params);
  332.                 return null;
  333.             }
  334.         }
  335.         $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  336.             sprintf('Import Object %s', ($object->getId() ? $object->getFullPath() : 'new')), $params);
  337.         $this->eventDispatcher->dispatch($definition'data_definitions.import.object.start'$object$params);
  338.         if ($definition->getRunner()) {
  339.             $runner $this->runnerRegistry->get($definition->getRunner());
  340.         }
  341.         if ($runner instanceof RunnerInterface) {
  342.             if ($runner instanceof DataSetAwareInterface) {
  343.                 $runner->setDataSet($dataSet);
  344.             }
  345.             if ($runner instanceof LoggerAwareInterface) {
  346.                 $runner->setLogger($this->logger);
  347.             }
  348.             $runner->preRun($object$data$definition$params);
  349.         }
  350.         $this->logger->info(sprintf('Imported Object: %s'$object->getRealFullPath()));
  351.         /**
  352.          * @var $mapItem ImportMapping
  353.          */
  354.         foreach ($definition->getMapping() as $mapItem) {
  355.             $value null;
  356.             if (array_key_exists($mapItem->getFromColumn(), $data) || $mapItem->getFromColumn() === "custom") {
  357.                 $value $data[$mapItem->getFromColumn()];
  358.                 $this->setObjectValue($object$mapItem$value$data$dataSet$definition$params$runner);
  359.             }
  360.         }
  361.         $shouldSave true;
  362.         if ($runner instanceof SaveRunnerInterface) {
  363.             if ($runner instanceof DataSetAwareInterface) {
  364.                 $runner->setDataSet($dataSet);
  365.             }
  366.             if ($runner instanceof LoggerAwareInterface) {
  367.                 $runner->setLogger($this->logger);
  368.             }
  369.             $shouldSave $runner->shouldSaveObject($object$data$definition$params);
  370.         }
  371.         if ($shouldSave) {
  372.             $object->setUserModification($params['userId'] ?? 0);
  373.             $object->setOmitMandatoryCheck($definition->getOmitMandatoryCheck());
  374.             $object->save();
  375.             $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  376.                 sprintf('Imported Object %s'$object->getFullPath()), $params);
  377.         } else {
  378.             $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  379.                 sprintf('Skipped Object %s'$object->getFullPath()), $params);
  380.         }
  381.         $this->eventDispatcher->dispatch($definition'data_definitions.import.status',
  382.             sprintf('Imported Object %s'$object->getFullPath()), $params);
  383.         $this->eventDispatcher->dispatch($definition'data_definitions.import.object.finished'$object$params);
  384.         if ($runner instanceof RunnerInterface) {
  385.             if ($runner instanceof DataSetAwareInterface) {
  386.                 $runner->setDataSet($dataSet);
  387.             }
  388.             if ($runner instanceof LoggerAwareInterface) {
  389.                 $runner->setLogger($this->logger);
  390.             }
  391.             $runner->postRun($object$data$definition$params);
  392.         }
  393.         return $object;
  394.     }
  395.     /**
  396.      * @param Concrete                     $object
  397.      * @param ImportMapping                $map
  398.      * @param                              $value
  399.      * @param array                        $data
  400.      * @param ImportDataSetInterface|array $dataSet
  401.      * @param ImportDefinitionInterface    $definition
  402.      * @param                              $params
  403.      * @param RunnerInterface              $runner
  404.      */
  405.     private function setObjectValue(
  406.         Concrete $object,
  407.         ImportMapping $map,
  408.         $value,
  409.         $data,
  410.         $dataSet,
  411.         ImportDefinitionInterface $definition,
  412.         $params,
  413.         RunnerInterface $runner null
  414.     ) {
  415.         if ($map->getInterpreter()) {
  416.             try {
  417.                 $interpreter $this->interpreterRegistry->get($map->getInterpreter());
  418.                 if ($interpreter instanceof DataSetAwareInterface) {
  419.                     $interpreter->setDataSet($dataSet);
  420.                 }
  421.                 if ($interpreter instanceof LoggerAwareInterface) {
  422.                     $interpreter->setLogger($this->logger);
  423.                 }
  424.                 try {
  425.                     $value $interpreter->interpret(
  426.                         $object,
  427.                         $value,
  428.                         $map,
  429.                         $data,
  430.                         $definition,
  431.                         $params,
  432.                         $map->getInterpreterConfig()
  433.                     );
  434.                 }
  435.                 catch (UnexpectedValueException $ex) {
  436.                     $this->logger->info(sprintf('Unexpected Value from Interpreter "%s" with message "%s"'$map->getInterpreter(), $ex->getMessage()));
  437.                 }
  438.             } catch (DoNotSetException $ex) {
  439.                 return;
  440.             }
  441.         }
  442.         if ($map->getToColumn() === 'o_type' && $map->getSetter() !== 'object_type') {
  443.             throw new \InvalidArgumentException('Type has to be used with ObjectType Setter!');
  444.         }
  445.         $shouldSetField true;
  446.         if ($runner instanceof SetterRunnerInterface) {
  447.             if ($runner instanceof DataSetAwareInterface) {
  448.                 $runner->setDataSet($dataSet);
  449.             }
  450.             if ($runner instanceof LoggerAwareInterface) {
  451.                 $runner->setLogger($this->logger);
  452.             }
  453.             $shouldSetField $runner->shouldSetField($object$map$value$data$definition$params);
  454.         }
  455.         if (!$shouldSetField) {
  456.             return;
  457.         }
  458.         if ($map->getSetter()) {
  459.             $setter $this->setterRegistry->get($map->getSetter());
  460.             if ($setter instanceof SetterInterface) {
  461.                 if ($setter instanceof DataSetAwareInterface) {
  462.                     $setter->setDataSet($dataSet);
  463.                 }
  464.                 if ($setter instanceof LoggerAwareInterface) {
  465.                     $setter->setLogger($this->logger);
  466.                 }
  467.                 $setter->set($object$value$map$data);
  468.             }
  469.         } else {
  470.             $object->setValue($map->getToColumn(), $value);
  471.         }
  472.     }
  473.     /**
  474.      * @param ImportDefinitionInterface     $definition
  475.      * @param                               $data
  476.      * @param                               $params
  477.      * @return Concrete
  478.      * @throws \Exception
  479.      */
  480.     private function getObject(ImportDefinitionInterface $definition$data$params)
  481.     {
  482.         $class $definition->getClass();
  483.         $classObject '\Pimcore\Model\DataObject\\'.ucfirst($class);
  484.         $classDefinition ClassDefinition::getByName($class);
  485.         $obj null;
  486.         if (!$classDefinition instanceof ClassDefinition) {
  487.             throw new \InvalidArgumentException(sprintf('Class not found %s'$class));
  488.         }
  489.         /**
  490.          * @var $loader LoaderInterface
  491.          */
  492.         if ($definition->getLoader()) {
  493.             $loader $this->loaderRegistry->get($definition->getLoader());
  494.         } else {
  495.             $loader $this->loaderRegistry->get('primary_key');
  496.         }
  497.         $obj $loader->load($class$data$definition$params);
  498.         if (null === $obj) {
  499.             $classImplementation $this->modelFactory->getClassNameFor($classObject) ?? $classObject;
  500.             $obj = new $classImplementation();
  501.         }
  502.         $key Service::getValidKey($this->createKey($definition$data), 'object');
  503.         if ($definition->getRelocateExistingObjects() || !$obj->getId()) {
  504.             $obj->setParent(Service::createFolderByPath($this->createPath($definition$data)));
  505.         }
  506.         if ($definition->getRenameExistingObjects() || !$obj->getId()) {
  507.             if ($key && $definition->getKey()) {
  508.                 $obj->setKey($key);
  509.             } else {
  510.                 $obj->setKey(File::getValidFilename(uniqid()));
  511.             }
  512.         }
  513.         if (!$obj->getKey()) {
  514.             throw new \InvalidArgumentException('No key set, please check your import-data');
  515.         }
  516.         $obj->setKey(Service::getUniqueKey($obj));
  517.         return $obj;
  518.     }
  519.     /**
  520.      * @param ImportDefinitionInterface $definition
  521.      * @param array                     $data
  522.      * @return string
  523.      */
  524.     private function createPath(ImportDefinitionInterface $definition$data)
  525.     {
  526.         $placeholderHelper = new Placeholder();
  527.         return $placeholderHelper->replacePlaceholders($definition->getObjectPath(), $data);
  528.     }
  529.     /**
  530.      * @param ImportDefinitionInterface $definition
  531.      * @param array                     $data
  532.      * @return string
  533.      */
  534.     private function createKey(ImportDefinitionInterface $definition$data)
  535.     {
  536.         $placeholderHelper = new Placeholder();
  537.         return $placeholderHelper->replacePlaceholders($definition->getKey(), $data);
  538.     }
  539. }