vendor/shopware/core/Content/ImportExport/Event/Subscriber/ProductCategoryPathsSubscriber.php line 56

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\ImportExport\Event\Subscriber;
  3. use Shopware\Core\Content\Category\CategoryDefinition;
  4. use Shopware\Core\Content\ImportExport\Event\ImportExportBeforeImportRecordEvent;
  5. use Shopware\Core\Content\ImportExport\Exception\ProcessingException;
  6. use Shopware\Core\Content\Product\ProductDefinition;
  7. use Shopware\Core\Framework\Api\Sync\SyncBehavior;
  8. use Shopware\Core\Framework\Api\Sync\SyncOperation;
  9. use Shopware\Core\Framework\Api\Sync\SyncServiceInterface;
  10. use Shopware\Core\Framework\Context;
  11. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  14. use Shopware\Core\Framework\Feature;
  15. use Shopware\Core\Framework\Log\Package;
  16. use Shopware\Core\Framework\Uuid\Uuid;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Contracts\Service\ResetInterface;
  19. /**
  20.  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  21.  */
  22. #[Package('system-settings')]
  23. class ProductCategoryPathsSubscriber implements EventSubscriberInterfaceResetInterface
  24. {
  25.     private EntityRepositoryInterface $categoryRepository;
  26.     private SyncServiceInterface $syncService;
  27.     /**
  28.      * @var array<string, string>
  29.      */
  30.     private array $categoryIdCache = [];
  31.     /**
  32.      * @internal
  33.      */
  34.     public function __construct(EntityRepositoryInterface $categoryRepositorySyncServiceInterface $syncService)
  35.     {
  36.         $this->categoryRepository $categoryRepository;
  37.         $this->syncService $syncService;
  38.     }
  39.     /**
  40.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  41.      */
  42.     public static function getSubscribedEvents()
  43.     {
  44.         return [
  45.             ImportExportBeforeImportRecordEvent::class => 'categoryPathsToAssignment',
  46.         ];
  47.     }
  48.     public function categoryPathsToAssignment(ImportExportBeforeImportRecordEvent $event): void
  49.     {
  50.         $row $event->getRow();
  51.         $entityName $event->getConfig()->get('sourceEntity');
  52.         if ($entityName !== ProductDefinition::ENTITY_NAME || empty($row['category_paths'])) {
  53.             return;
  54.         }
  55.         $result = [];
  56.         $categoriesPaths explode('|'$row['category_paths']);
  57.         $newCategoriesPayload = [];
  58.         foreach ($categoriesPaths as $path) {
  59.             $categories explode('>'$path);
  60.             $categoryId null;
  61.             foreach ($categories as $currentIndex => $categoryName) {
  62.                 if (empty($categoryName)) {
  63.                     continue;
  64.                 }
  65.                 $partialPath implode('>', \array_slice($categories0$currentIndex 1));
  66.                 if (isset($this->categoryIdCache[$partialPath])) {
  67.                     $categoryId $this->categoryIdCache[$partialPath];
  68.                     continue;
  69.                 }
  70.                 $criteria = new Criteria();
  71.                 $criteria->addFilter(new EqualsFilter('name'$categoryName));
  72.                 $criteria->addFilter(new EqualsFilter('parentId'$categoryId));
  73.                 $category $this->categoryRepository->search($criteriaContext::createDefaultContext())->first();
  74.                 if ($category === null && $categoryId === null) {
  75.                     break;
  76.                 }
  77.                 if ($category !== null) {
  78.                     $categoryId $category->getId();
  79.                     $this->categoryIdCache[$partialPath] = $categoryId;
  80.                     continue;
  81.                 }
  82.                 $parentId $categoryId;
  83.                 $categoryId Uuid::fromStringToHex($partialPath);
  84.                 $this->categoryIdCache[$partialPath] = $categoryId;
  85.                 $newCategoriesPayload[] = [
  86.                     'id' => $categoryId,
  87.                     'parent' => ['id' => $parentId],
  88.                     'name' => $categoryName,
  89.                 ];
  90.             }
  91.             if ($categoryId !== null) {
  92.                 $result[] = ['id' => $categoryId];
  93.             }
  94.         }
  95.         if (!empty($newCategoriesPayload)) {
  96.             $this->createNewCategories($newCategoriesPayload$row['category_paths']);
  97.         }
  98.         $record $event->getRecord();
  99.         $record['categories'] = !empty($record['categories']) ? array_merge($record['categories'], $result) : $result;
  100.         $event->setRecord($record);
  101.     }
  102.     public function reset(): void
  103.     {
  104.         $this->categoryIdCache = [];
  105.     }
  106.     /**
  107.      * @param list<array<string, mixed>> $payload
  108.      */
  109.     private function createNewCategories(array $payloadstring $categoryPaths): void
  110.     {
  111.         if (Feature::isActive('FEATURE_NEXT_15815')) {
  112.             $behavior = new SyncBehavior();
  113.         } else {
  114.             $behavior = new SyncBehavior(truetrue);
  115.         }
  116.         $result $this->syncService->sync([
  117.             new SyncOperation(
  118.                 'write',
  119.                 CategoryDefinition::ENTITY_NAME,
  120.                 SyncOperation::ACTION_UPSERT,
  121.                 $payload
  122.             ),
  123.         ], Context::createDefaultContext(), $behavior);
  124.         if (Feature::isActive('FEATURE_NEXT_15815')) {
  125.             // @internal (flag:FEATURE_NEXT_15815) - remove code below, "isSuccess" function will be removed, simply return because sync service would throw an exception in error case
  126.             return;
  127.         }
  128.         if (!$result->isSuccess()) {
  129.             $operation $result->get('write');
  130.             throw new ProcessingException(sprintf(
  131.                 'Failed writing categories for path %s with errors: %s',
  132.                 $categoryPaths,
  133.                 $operation json_encode(array_column($operation->getResult(), 'errors')) : ''
  134.             ));
  135.         }
  136.     }
  137. }