Magento2: Product::addImageToMediaGallery throws Exception

Created on 29 Sep 2016  路  53Comments  路  Source: magento/magento2

Hi folks,
I'm trying to add some images to a product, but it's not working like expected.

Preconditions

Magento Version 2.1.1
PHP Version 7.0

Steps to reproduce

$product = $this->productFactory->create()
            ->setName($productName)
            ->setStatus($productStatus)
            ->setSku($productSku)
$product->setAttributeSetId($product->getDefaultAttributeSetId());

$product->addImageToMediaGallery($file, [
                'image',
                'small_image',
                'thumbnail',
            ], false, false);
$this->productRepository->save($product);

Expected result

Image gets added

Actual result

Exception gets thrown:

Notice: Undefined index: media_type in /var/www/magento.fissler.local/vendor/magento/module-catalog/Model/Product.php on line 2527

There's a workaround for this issue, using the Product::save method instead of the ProductRepositoryInterface::save method, but because it's a deprecated method I would like to avoid this.

Catalog Fixed in 2.2.x Fixed in 2.3.x Clear Description Confirmed Format is valid Ready for Work Reproduced on 2.1.x Reproduced on 2.2.x Reproduced on 2.3.x bug report help wanted

Most helpful comment

@veloraven, this issue is present in Magento CE 2.1.6.

Do you require any more replication guidance to get this labeled as "acknowledged" and moved to an internal ticket?

All 53 comments

I've encountered an additional bug, when using the Product::save-workaround:

function addImages() {
    $product = $this->productFactory->create()
            ->setName($productName)
            ->setStatus($productStatus)
            ->setSku($productSku)
    $product->setAttributeSetId($product->getDefaultAttributeSetId());

    $product->addImageToMediaGallery($file, [
                'image',
                'small_image',
                'thumbnail',
            ], false, false);
    $product->save()

    // Fake the behavior of ProductRepository::save
    return $this->productRepository->get($product->getSku());
}

$product = $this->addImages();
$this->productRepository->save($product);

The ProductRepository::save call removes all media gallery entries.

The same problem for me - strange is that is was working previously but it stopped after base url for media files was changed

The same problem here, any chance to fix it?

Same here, 3 months gone and no response ?!
@magento-team Please fix that, otherwise this method makes absolutely no sense if we cant use it the preferred ( new ) way.

hello @sparrowek, @Szpadyzor, @eduard-kistner, could you please clarify which versions do you use?

@KrystynaKabannyk version 2.1.1, PHP 7.0, like in 1st post.

Hi @KrystynaKabannyk,

shure:

  • Magento ver. 2.1.2
  • PHP 5.6.27-0+deb8u1 ( on Docker Dev box )

In my humble opinion the only missing part is that in
Magento\Catalog\Model\Product\Gallery\Processor -> addImage(...)
there will be no media_type set in the $mediaGalleryData array elements.

The workaround with deprecated save() ( mainly ) works for me.
So i get the images in backend but not assigned to image / thumbnail etc. ( havent debugged it yet ).

I did some more research as the change mentioned above does not work completely well.

It will seems to fit as no more exception will be thrown, but no images will be shown on product side then.
So there is some more ( confusing ) stuff going on when the ProductRepository saves an product.

The main Problem seems then to be that the method processMediaGallery() will not recognize entries which correspond to an file.
If the image is new it only will add entries to the product which have the content key set.
Also there seems to be lot of setting / unsetting of product media items, which maybe have there use case but i could not find them.

In the end i have created an module to fix that.
Its possible that i have not taken all cases into account ( hopefully @magento-team will do complete refactoring ) but for me it works.

Also ran into this problem today.
I don't want to fallback to $product->save(); so @eduard-kistner thx for your module!
I ran into a similar issue when saving categories:
http://magento.stackexchange.com/questions/161021/category-attribute-not-saving-with-repointerface

If this is not in it's correct place here, I'm sorry

I am unable to get this working either, having tried a variety of approaches. Here's what I have tried:

  • Using $productRepo->save($product)
  • Using $productRepo->save($product) with @eduard-kistner module
  • Using $product->save() followed by $productRepo->save($product)
  • Using $product->save() followed by $productRepo->save($product) with @eduard-kistner module

Nothing has worked. I'm on Magento 2.1.5, PHP 7.0.15

The only thing I seem to be doing differently than others is setting the $move parameter to true, but I don't think that should matter, based on the comments of Magento\Catalog\Model\Product\Gallery\Processor::addImage. I tried setting $move to false to no avail as well.

What happens for me is an image is associated with the product, but none of 'roles' are set in the admin when I view the product.
image-roles-not-set

If I set the roles manually in the admin, after a programmatic load, it seems to work. Strangely the admin doesn't seem to be using Magento\Catalog\Model\Product\Gallery\Processor::addImage, because I put a die statement at the top of the function and the save operation in the admin went through without a problem.

That said, I haven't taken the time to break out the debugger to see what kind of workaround might actually work, though I'm sure there is one lurking behind a debug session.

@quickshiftin
Updated to 2.1.5 and with PHP 7.0.15 / nginx but could not find an problem with setting the media attribute like
$product->addImageToMediaGallery('/path/to/image.jpg', ['image', 'small_image', 'thumbnail', 'swatch_image'], true, false);

But maybe we should not spam this ticket ;)

@eduard-kistner
I just did a fresh install of Magento 2.1.5 PHP 7.0.8 / apache. Without your extension I'm still getting the error message Notice: Undefined index: media_type. Once I install your extension, setting media attributes still does not work.

I went ahead an made a module to demonstrate the problem. Maybe there's a glaring error in there you could point out :)

@veloraven, this issue is present in Magento CE 2.1.6.

Do you require any more replication guidance to get this labeled as "acknowledged" and moved to an internal ticket?

Any update on this?

I am sorry all of You to hear that, but You all do it wrong.
And it is not a bug.
Just simple debbuging will let You know how it work.

First You have to create media entry:

const MEDIA_TYPE_IMAGE = 'image';

protected function createMediaImagesEntries($images)
    {
        $entries = [];

        foreach ($images as $imagePath) {
            /** @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaEntry **/
            $mediaEntry = $this->entryFactory->create();

            $mediaEntry->setMediaType(self::MEDIA_TYPE_IMAGE);

            $entries[] = $mediaEntry;
        }

        return $entries;
    }

Then You have to set it on product:

$entries = $this->createMediaImagesEntries($images);
$product->setMediaGalleryEntries($entries);

and then You have to add image to media gallery:

$product->addImageToMediaGallery($filepath, null, true, false);

@Szpadyzor

Seriously, are you just trolling?
The mentioned code will IMO not work when i will use \Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface for $this->entryFactory->create(); as the create method needs an product sku and an existing \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface object.

Sp maybe you can clarify what methods exactly you are using?

@eduard-kistner sry Man, my bad:

should be \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory instead of \Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface. I am correcting it.

@Szpadyzor

Thanks for the clarification but your code does also throw the mentioned exception ( tested with Magento 2.1.7 ).
The Problem is and always was the addImageToMediaGallery() method and it doesn't get better if you set more entries ( which will be empty ).

I will try to give an step by step example what i mean:

  1. In your createMediaImagesEntries() method you are creating an array of empty Instances of \Magento\Catalog\Model\Product\Gallery\Entry class.
  2. You set this array via the product class setMediaGalleryEntries() method.
    This method will convert the Gallery/Entry instances to arrays which is generally fine ( if its clean is another question ) and set this multidimensional array as products media_gallery['images'].
  3. Now you are using $product->addImageToMediaGallery($filepath, null, true, false); to set the real image.
    But this will not use the data of the Gallery/Entry which you set previous, it could not do that!
    And it could not do that because the code in Magento\Catalog\Model\Product\Gallery\Processor::addImage() on line 181 will add an new array to products media_gallery['images'] data.
  4. In the end you have kind of this array set ( no Instances of any class will ever be set to the product data ):
'media_gallery' => [
    'images' => [
        0 => [
            'value_id' = null,
            'file' = null,
            'label' = null,
            'position' = null,
            'disabled' = null,
            'types' = null,
            'media_type' = 'image',
            'content' = null,
        ],
        1 => [
            'file' = 'some_path',
            'label' = '',
            'position' = 1,
            'disabled' = 0,
         ]
    ]
]
  1. Therefore the Exception mentioned in this issue gets thrown.

So in my opinion its surly generally better to use the Gallery/Entry abstractions.
But then the $product->addImageToMediaGallery() method must be changed / declared deprecated / never used anymore.

Also as this method will be the one mostly used IMO its not an solution to shout that everyone is doing it false but to fix it the way everyone uses it ( or remove that ).

All in all @magento-team should refactor this as mentioned before ;)

anyone were able to add an image to product programmatically?

@Dukeofvampires,

Yes:

1

Install this: https://github.com/DIE-KAVALLERIE/magento2-product-image-fix

2

/**
 * @var \Magento\Catalog\Model\ProductRepository
 */
protected $productRepository;

/**
 * @var MagentoProduct\Gallery\Processor
 */
protected $galleryProcessor;

...

// Delete existing Magento images from DB and filesystem
$existingMedia = $product->getMediaGalleryEntries();
if(is_array($existingMedia) && count($existingMedia) > 0) {
    foreach($existingMedia as $existingMediaItem) {
        $this->galleryProcessor->removeImage($product, $existingMediaItem['file']);
    }
    // @todo: delete images without saving here. Each Magento save is computationally expensive.
    $this->productRepository->save($product);
}

// Import new images
$product->setMediaGalleryEntries([]);
$newImages = [...];
foreach($newImages as $i => $newImage) {
    $imageRoles  = $i == 0 ? ['image', 'small_image', 'thumbnail'] : [];
    $product->addImageToMediaGallery($newImage['local_path'], $imageRoles, true, false);
}
$this->productRepository->save($product);

Thank you, this will do as a temporary solution.
But I generally don't like the idea of overriding product repository :(

Also how magento itself is saving uploaded images through admin?
I'm currently trying to debug, but with all those ajax calls it's taking longer then expected.

@Dukeofvampires, as with most issues on magento2, there's probably a year-old fix in develop branch that hasn't been pushed to release :(

I don't know how Magento admin gets round it. Due to time constraints, I gave up debugging and applied @eduard-kistner's patch.

@JacobDrummond
As far as I understood, magento itself is using resourced directly. Probably this way saving is not affected by Repository bug.

That's a problem with M2 using deprecated methods in it's core.

So probably I would prefer using patch as well.

@Dukeofvampires, not ResourceModel->save(), but Model->save(). Which is still pretty bad, considering AbstractModel->save() is deprecated.

I see this all the time throughout the codebase: Magento devs breaking their own rules. I would guess this core stuff was written before the coding standards were finalised.

well, yes, thats what I meant. Technically Model->save(); is a shortcut to $this->_getResource()->save($this) ;)

I'm pretty sure most of this is present because lots of the code is ported from M1.

but it's better to stop flooding the issue. :)

@quickshiftin I need to know if you find any solution to your problem. I have the same issue, image gets added to product but no image role it's set

This is still a big issue withing Magento 2.1.8

Exactly same in here. M2.1.8

I got this to work for everything except for PNG's. But its way to hard to add images ...

see : https://github.com/magento/magento2/issues/11129#issuecomment-333062193

@R4c00n, thank you for your report.
We've created internal ticket(s) MAGETWO-81589 to track progress on the issue.

jignesh26 #mm18in

@jignesh26 thank you for joining. Please accept team invitation here and self-assign the issue.

@R4c00n I am checking this issue. As you have told us that Product::save method is deprecated. So logically only one to use the product save using ProductRepositoryInterface. I am debugging this issue and will update you soon. Meanwhile, if anyone has any idea related to this ticket can share that help me to resolve it fast.

@p-bystritsky you have made commit what is that status?

here is the solution to resolve the issue.

vendor\magento\module-catalog\Model\Product\Gallery\Processor.php Please override the file in your theme.

Please search for

$mediaGalleryData['images'][] = [ 'file' => $fileName, 'position' => $position, 'label' => '', 'disabled' => (int)$exclude, ];

and replace with

$mediaGalleryData['images'][] = [ 'file' => $fileName, 'position' => $position, 'label' => '', 'disabled' => (int)$exclude, 'media_type' => 'image', 'types' => $mediaAttribute ];

I have just added media_type and types fields here.

I have tested and it's working fine.

@lokeg888 , I did what you suggested but still no luck, I get content not valid

@ishakhsuvarov you have unassigned to me from the issue. Actually, I was looking for the help to resolve this issue. Can you tell me if anyone another gone resolve this issue?

@jignesh26 Didn't expect you are still working on it :) Sorry

@ishakhsuvarov No issue. can you help me to resolve this issue? actually, I need support from any magneto core team. I am trying to resolve this issue. Its very hard issue to get resolved. I have questions in my mind.
Let me know if you have any core team member contact to help me to resolve this issue.

I can understand @magento-engcom-team have many works to do.

@magento-engcom-team
Sor , I am facing the same issue on 2.2.1 version.

Working solution from production system:

  1. Prepare images entries:

`

protected function createMediaImagesEntries($images)
{

    $entries = [];

    foreach ($images as $key => $value) {
        $fileTitle = $value['title'] ?? null;
        /** @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory; $mediaEntry */
        $mediaEntry = $this->entryFactory->create();

        $mediaEntry->setMediaType('image');
        $mediaEntry->setLabel($fileTitle);

        $entries[] = $mediaEntry;
    }

    return $entries;
}`
  1. Add images to product gallery

`

    protected function addImagesToProductGallery(ProductInterface $product, $entries, $files, $loggerPath)
    {

    $data = [];

    foreach ($files as $key => $file) {
        $entry = $entries[$key];
        $imageContent = $this->getImageContent($file['file'], $loggerPath);

        if (!$imageContent) {
            unset($entries[$key]);
            continue;
        }

        $fileInfo = pathinfo($file['file']);
        $fileData = $this->remover->resolveFilePrefix($file['file']);
        $filename = $fileData['prefix'] . $fileInfo['basename'];

        try {
            if (0 === $key) {
                $product->addImageToMediaGallery($file['file'], ['image', 'small_image', 'thumbnail'], true, false);
            } else {
                $product->addImageToMediaGallery($file['file'], null, true, false);
            }

            $entry->setPosition($key);
            $entry->setFile($filename);
            $entry->setContent($imageContent);
            $entries[$key] = $entry;
        } catch (LocalizedException $ex) {
            $this
                ->integratorHelper
                ->logger(
                    $loggerPath,
                    sprintf('Probably image %s did not exist, exception: %s', $file['file'], $ex->getMessage())
                );
        }
    }

    $product->setMediaGalleryEntries($entries);
    $data['product'] = $product;

    return $data;
}`

After that save product object and that's all.

Working on version 2.1.7.

Thanks for the update @szpadyzor

Thanks for @Szpadyzor ,but Actually the code in my project didn't work .
I created a new Repo to fix this problem , It always work find in my project.
Can any one give me some suggest ? I am a new in Magento 2 .

here is the Repo link , but just have Chinese version ..
https://github.com/GraysonAstral/ProductImageFix

@GraysonAstral 404 on link.

@Szpadyzor Thanks , I modify the repo to public . Now it's Available.

@GraysonAstral still don't know why you want to overwrite core magento mechanism. I gave you working solution.

And what does it mean that "code in my project didn't work" ? Some exception is thrown or what?

Don't know why @Szpadyzor comes again with not working code examples ( see my comment here ) but did ya tried https://github.com/DIE-KAVALLERIE/magento2-product-image-fix ?

I will tell you why, because @eduard-kistner never test my implementation, you wrote:
"The mentioned code will IMO not work"
I will not show you the whole code because it is property of my client.
Did you @eduard-kistner ever debug addImage method? Did you ever try to understand this exception? Do you know why it works from admin panel?

There is said:
Notice: Undefined index: media_type in /var/www/magento.fissler.local/vendor/magento/module-catalog/Model/Product.php on line 2527

To simply debug it, just check how it is done in admin panel.

Product on adminhtml is saved by ProductRepository;
There is in ProductRepository class, method save.
In this method in line 518 there is:
$this->processMediaGallery($product, $productDataArray['media_gallery_entries']);

Do you know what it does? It creates media gallery entries. In line 478 in ProductRepository:
$this->processNewMediaGalleryEntry($product, $newEntry);
In this method media_type is set, so thats why You should do it too if you want your code work.

Show me your previous implementation, and show me where did you set up media_type property.
Probably You cant beacause you didnt do it.

You can create new media entry it is better to do it because you can set not only images but videos or other media types.

Maybe you saw that processMediaGallery check that is there any $mediaGalleryEntries set.

Debug method save from ProductRepository class and understand it.

I will tell it again: You dont have to overwrite magento Gallery Processor just understand the code.
If you are adding image to media gallery it should have set galleryEntry with media_type.

Of course saving product by $product->getResource->save($product) (yes i know this is deprecated in 2.2) dont use save from ProductRepository that's why it work without exception. But you must use ProductRepository for saving product to stick to magento rules.

Debug it @eduard-kistner and then argue with me and lever my solution.

@Szpadyzor
Did you ever read my post or tried my module?
Don't know why we always need 50 repos and 100 workarounds ( which mostly don't work or have incomplete examples ).

The mentioned module is long time on min. one Live instance and others also use it.

From now on i will simply ignore you till you have constructive informations.

@eduard-kistner
You can ignore my answers but i will warn others developers to do not use your module.
Do you know why? Because you overwrite magento core files. It is not acceptable to do it, and you know why @eduard-kistner ? Because if i have modules that create plugins (Do you know what it is?) after, before and around method save from ProductRepository they will stop works. I dont even want to think what if someone have modules from other vendors that do something on ProductRepository save plugins. They will have to overwrite all modules to work with your poor module.
And for clarity, You create overwrite "module", You create workaround. I just gave good practice solution without overwriting anything.

And your module? With manual creating directories? Haha! It is good - how you said "trolling". Did you ever use continous integration? Did you ever have to create project from scrath multiple times? I dont want to even think about your solution if i will have to involve 20 developers to project and told them how to "install" your module. How many this kind of modules do you have in your project? 10, 20? With manually creating directories? Copy files from repository?
Missing only:
"Next step: copy files by ftp to your production environment."

Hello Folks,

I am trying to resolve this issue with flow the way it should work its taking time more than usual. this only for update from my side if any buddy resolved this or found any way to resolve this by stranded way Lets us know here please.

Hi @progreg. Thank you for working on this issue.
Looks like this issue is already verified and confirmed. But if your want to validate it one more time, please, go though the following instruction:

  • [x] 1. Add/Edit Component: XXXXX label(s) to the ticket, indicating the components it may be related to.
  • [x] 2. Verify that the issue is reproducible on 2.3-develop branch

    Details- Add the comment @magento-engcom-team give me 2.3-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.3-develop branch, please, add the label Reproduced on 2.3.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and _stop verification process here_!

  • [x] 3. Verify that the issue is reproducible on 2.2-develop branch.

    Details- Add the comment @magento-engcom-team give me 2.2-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.2-develop branch, please add the label Reproduced on 2.2.x

  • [ ] 4. If the issue is not relevant or is not reproducible any more, feel free to close it.

Hi @R4c00n. Thank you for your report.
The issue has been fixed in magento/magento2#18952 by @progreg in 2.3-develop branch
Related commit(s):

The fix will be available with the upcoming 2.3.1 release.

Hi @R4c00n. Thank you for your report.
The issue has been fixed in magento/magento2#18951 by @progreg in 2.2-develop branch
Related commit(s):

The fix will be available with the upcoming 2.2.8 release.

Was this page helpful?
0 / 5 - 0 ratings