8

I need to do a massive update of product data but what I need to do can't be achieved with product import. In example I need to update media gallery and categories for given products, but the solution I came out with takes too long.

A little recap: I added a command to the Magento 2 CLI that, given a json configuration file, removes, adds, updates or sorts media gallery entries for a given product like this. Here I paste an excerpt of the code:

/* $product is of type Magento\Catalog\Model\Product */

//get existing media gallery
$existingMediaGallery = $product->getMediaGallery();

/* 
   do stuff with media gallery (alter $existingMediaGallery)
   (add, remove, sort, ...)
*/

//set media gallery again
$product->setMediaGallery($existingMediaGallery);

//process media gallery
$mediaGalleryEntries = $product->getMediaGalleryEntries();
$this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
if ($mediaGalleryEntries) {
  foreach ($mediaGalleryEntries as $k => $entry) {
    if (!isset($entry['removed']) && !empty($entry['types'])) {
      $this->getMediaGalleryProcessor()->setMediaAttribute($product, $entry['types'], $entry['file']);
    }
  }
}

//save product
$product->save();

Since it's a massive update the "$product->save()" line is called many times and always takes from 2 to 4 seconds. Since I need to launch the code for thousands of products I need a faster way to do that.

I tried with

$product->getResource()->saveAttribute($product, 'media_gallery');

and

$product->addAttributeUpdate('media_gallery', $mediaGallery, $storeId);

but that doesn't work for media gallery (only works for eav I think).

Is there a way to save only media gallery and persisting these changes faster?

(What I look for is something like Magento\Catalog\Api\CategoryLinkManagementInterface::assignProductToCategories method that saves category/product association faster than a full product save)

0

I think you are on the right track: the save product is taking long and needs to go..

Now, with images update, that can be really tricky. But I'd like to think it may be a way forward to separate the problem:

  • my guess is you need the media data to be stored in the database and that can be done by using your method saveAttribute.. (very fast). However, you may want to add some attributes in your update script: see attributes 'image, small_image, thumbnail' (see in database select * from eav_attribute where ((attribute_code like '%image%') or (attribute_code='thumbnail')) and entity_type_id=4))

  • now the more complex but yet I may have some ideas, is to deal with the image physically speaking and possible extra media data (image tag title, position and so on)

--> for this second point: I'd look at the class Magento\Catalog\Model\Product\Gallery\CreateHandler as it will show you how Magento keeps it all working regarding the media data.

0

As far as I know there isn't a way to save media entries without saving product. but the save product shouldn't take very long at all with the productRepositoryInterface.

I created a similar module and I don't have the same issue with media save.

Here is my solution for save media:

 /**
     * Add Product media from folder.
     * @param $product
     * @param $productData
     * @return mixed
     */
    protected function setProductMedia($product, $productData)
    {
        $medias = [];
        $files = scandir(self::MEDIA_PATH);
        $mediaPath = '';
        foreach ($files as $file) {
            if ($file !== '.' && $file !== '..') {
                $path = realpath(self::MEDIA_PATH . $file);
                if ($path) {
                    if (basename($path, '.jpg') == trim($productData['productCode'])) {
                        $mediaPath = self::MEDIA_PATH . $file;
                        break;
                    }
                }
            }
        }

        if ((bool)$mediaPath) {
            $image = $this->_imageContent
               ->setBase64EncodedData(base64_encode(file_get_contents($mediaPath)))
               ->setType(image_type_to_mime_type(exif_imagetype($mediaPath)))
               ->setName(basename($mediaPath));

            $media = $this->_productAttributeMediaGalleryEntry
               ->setFile($mediaPath)
               ->setTypes(['thumbnail', 'small_image', 'image'])
               ->setLabel($productData['description'])
               ->setPosition(0)
               ->setMediaType('image')
               ->setContent($image)
               ->setDisabled(false)
               ->setPosition(0);

            $medias[] = $media;
            $product->setMediaGalleryEntries($medias);
       }

        return $product;
    }

I then save the product with the productRepositoryInterface.

   try {
        $product = $this->_productRepository->get($productData['productCode']);
    } catch (NoSuchEntityException $e) {
        throw new NoSuchEntityException(__('Could Not Update Product Error: %1', $e->getMessage()));
    }

    $product = $this->setProductMedia($product, $productData);

    try {
       $this->_productRepository->save($product);
    } catch (InputException $exception) {
       $this->_logger->critical(__("Could not save product Error: %1", $exception->getMessage()));
    } catch (StateException $exception) {
       $this->_logger->critical(__("Could not save product, Error: %1",$exception->getMessage()));
    } catch (CouldNotSaveException $exception) {
       $this->_logger->critical(__('Could Not Save Product Error: %1' ,$exception->getMessage()));
    }

It saves pretty quickly. I can run 10,000 products within a few minutes. That includes the search for the media that is named as the {sku}.jpg and all stuffed in a single folder.

  • is it possible to just set image roles i.e. ->setTypes(['thumbnail', 'small_image', 'image']) without replacing the entire image? – paj Sep 7 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.