phpshopwareshopware6

How to import products with variations in Shopware 6


I'm trying to import products from an XML with variations. The import for the products works so far but it doesn't create the variations.

Here is my code (simplified):

    /**
     * @return int
     * @throws \Exception
     */
    public function execute()
    {
        // avoid reaching memory limit
        ini_set('memory_limit', '-1');

        // set tax id
        $this->setTaxId();
        if (empty($this->taxId)) {
            return 1;
        }

        // read products from import xml file
        $importProducts = $this->loadProducts();
        $csvBatch = array_chunk($importProducts, self::BATCH);
        $productNumbers = [];

        foreach ($csvBatch as $products) {
            $productNumbers[] = $this->processImportProducts($products, false);
        }

        $this->deleteProducts(array_merge(...$productNumbers));

        return 0;
    }

    /**
     * @param $productsData
     * @param $progressBar
     * @return array
     */
    private function processImportProducts($productsData, $progressBar)
    {
        $products = [];
        $productNumbers = [];

        foreach ($productsData as $product) {
            $products[$product['SKU']['@cdata']] = $this->importProducts($product, $progressBar);
            $productNumbers[] = $product['SKU']['@cdata'];
        }


        // upsert product
        try {
            $this->cleanProductProperties($products, $this->context);
            $this->productRepository->upsert(array_values($products), $this->context);
        } catch (WriteException $exception) {
            $this->logger->info(' ');
            $this->logger->info('<error>Products could not be imported. Message: '. $exception->getMessage() .'</error>');
        }
        unset($products);

        return $productNumbers;
    }

    /**
     * @param $product
     * @param $progressBar
     * @return array
     */
    private function importProducts($product, $progressBar)
    {
         ...

         $productData = [
            'id' => $productId,
            'productNumber' => $productNumber,
            'price' => [
                [
                    'currencyId' => Defaults::CURRENCY,
                    'net' => !empty($product['net']) ? $product['net'] : 0,
                    'gross' => !empty($product['net']) ? $product['net'] : 0,
                    'linked' => true
                ]
            ],
            'stock' => 99999,
            'unit' => [
                'id' => '3fff95a8077b4f5ba3d1d2a41cb53fab'
            ],
            'unitId' => '3fff95a8077b4f5ba3d1d2a41cb53fab',
            'taxId' => $this->taxId,
            'name' => $productNames,
            'description' => $productDescriptions
        ];

        if(isset($product['Variations'])) {
            $variationIds = $product['Variations']['@cdata'] ?? '';
            $productData['variation'] = [$this->getProductVariationIds($variationIds)];
        }

        return $productData;
    }

    /**
     * Get product variation ids
     *
     * @param string $productVariations
     * @return string
     */
    private function getProductVariationIds($productVariations)
    {
        $productVariationIds = explode(',', $productVariations);
        // get product variationIds in form of a string list
        $ids = $this->productRepository->search(
            (new Criteria())->addFilter(new EqualsAnyFilter('productNumber', $productVariationIds)),
            $this->context
        )->getIds();

        return implode(',', $ids);
    }

It loads correctly the ids but nothing happen. Also no error.

Anyone an idea how to import variations as well?


Solution

  • The variation field is not meant to be persisted or to create variants of a product. It has the Runtime flag, meaning it's not an actual database column but processed during runtime.

    You have to create/update variants just like you create the parent product. Additionally you have to set the parentId and the options. The latter being associations to property_group_option, which you'll have to create first.

    So in addition to your existing payload when creating parent products, you'll have to add this data to the variants:

    $productData = [
        // ...
        'parentId' => '...',
        'options' => [
            ['id' => '...'],
            ['id' => '...'],
            ['id' => '...'],
            // ...
        ],
    ];
    

    Finally you'll have to create the product_configurator_setting records. That's one record for each option used across all variants. Also the productId for the records has to be the one of the parent product.

    $repository = $this->container->get('product_configurator_setting.repository');
    
    $configuratorSettings = [];
    foreach ($options as $option) {
        $configuratorSetting = [
            'optionId' => $option['id'],
            'productId' => $parentId,
        ];
    
        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('productId', $parentId));
        $criteria->addFilter(new EqualsFilter('optionId', $option['id']));
    
        $id = $repository->searchIds($criteria, $context)->firstId();
    
        // if the configurator setting already exists, update or skip
        if ($id) {
            $configuratorSetting['id'] = $id;
            // continue;
        }
    
        $configuratorSettings[] = $configuratorSetting;
    }
    
    $repository->upsert($configuratorSettings, $context);