Magento 2 – Product Information Not Updating in Admin Edit

configurable-productmagento2massactionPHP

Summarized Problem:

When updating product information in Admin product/inventory/catalog/edit page, information is not persisted at all after clicking "Save" button.

The Context:

I added a mass action to product/inventory/catalog.

In app/code/Vendor/Module/view/adminhtml/ui_component/product_listing.xml

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <listingToolbar name="listing_top">
        <massaction name="listing_massaction">
            <action name="create_configurable">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Create Configurable</item>
                            <item name="message" xsi:type="string" translate="true">Create configurable for selected items?</item>
                        </item>
                        <item name="type" xsi:type="string">create_configurable</item>
                        <item name="label" xsi:type="string" translate="true">Create Configurable Product</item>
                        <item name="url" xsi:type="url" path="frontend/product/massConfigurable"/>
                    </item>
                </argument>
            </action>
        </massaction>
    </listingToolbar>
</listing>

And i implemented the mass action execute's method. The functionality is that given a list of simple products it would created a configurable product and associate those simple products to that configurable. Below is the code.

In app/code/Vendor/Module/Controller/Adminhtml/Product/MassConfigurable.php

/**
 * @return \Magento\Backend\Model\View\Result\Redirect
 */
public function execute()
{
    try {
        $collection = $this->filter->getCollection($this->collectionFactory->create());
        $associatedProductsIds = array();
        $configurableProductAttributesIds = array();
        $attributes = null;
        $attributeSetId = null;
        $notCreatedMessage = '';

        foreach ($collection->getItems() as $product) {
            if($product->getTypeId() == 'simple') {
                $associatedProductsIds[] = $product->getId();
                if(!$attributes) {
                    $attributes = $product->getAttributes(); //Gets the product's attributes defined by the user.
                    foreach ($attributes as $attribute) {
                        if (($attribute->getAttributeCode() != 'cost' && $attribute->getAttributeCode() != 'destacado') && ($attribute->getIsUserDefined() == 1))
                            $configurableProductAttributesIds[] = $attribute->getAttributeId();
                    }
                }
                if(!$attributeSetId) {
                    $attributeSetId = $product->getAttributeSetId();
                }
            }
        }

        if(sizeof($associatedProductsIds) <= 0) {
            $notCreatedMessage = 'There are not products to associate to a Configurable Product.';
        } else if(sizeof($configurableProductAttributesIds) <= 0) {
            $notCreatedMessage = 'There are not attributes to create a Configurable Product.';
        } else {
            if (sizeof($associatedProductsIds) != sizeof($collection->getItems())) {
                $notCreatedMessage = 'At least one product could not be associated to the Configurable Product.';
            }
            $this->createConfigurable($associatedProductsIds, $configurableProductAttributesIds, $attributeSetId);
            $this->messageManager->addSuccessMessage(
                __('A total of %1 record(s) have been associated.', sizeof($associatedProductsIds))
            );                
        }
        if($notCreatedMessage) {
            $this->messageManager->addWarningMessage(
                __($notCreatedMessage)
            );
        }
    } catch(Exception $e){
        $this->messageManager->addErrorMessage(
            __($e->getMessage())
        );
    }

    return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('catalog/*/index');
}

private function createConfigurable($products, $attributes, $attributeSetId) {
    $configurableId = time();
    $configurableProduct = $this->productFactory->create();
    $configurableProduct->setSku($configurableId);
    $configurableProduct->setName($configurableId);
    $configurableProduct->setUrlKey($configurableId);
    $configurableProduct->setAttributeSetId($attributeSetId);
    $configurableProduct->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED);
    $configurableProduct->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH);
    $configurableProduct->setWebsiteIds([$this->_storeManager->getWebsite()->getId()]);
    $configurableProduct->setStockData(['use_config_manage_stock' => 0, 'is_in_stock' => 1, 'manage_stock' => 1]);
    $configurableProduct->save();
    $cProductId = $this->_productRepository->get($configurableProduct->getSku())->getId();
    $this->assingProducts($cProductId, $products, $attributes);
}

private function assingProducts($productId, $products, $attributes) {
    /** Associates selected simple product to newly created configurable product */
    $product = $this->productFactory->create()->load($productId);
    $product->setTypeId('configurable');
    $product->setAssociatedProductIds($products);
    $product->getTypeInstance()->setUsedProductAttributeIds($attributes,  $product); //attributes IDs of product's attributes in my store
    $configurableAttributesData =  $product->getTypeInstance()->getConfigurableAttributesAsArray($product);
    $product->setCanSaveConfigurableAttributes(true);
    $product->setConfigurableAttributesData($configurableAttributesData);
    $product->save();
}

For this method i assume that all the products have the same group of attributes (color-size or color-size-texture, etc) and that they have different "group" of selected options for each one (black-small, black-big, etc).

Detailed Problem:

The Configurable Product is created successfully and the Simple Products are associated correctly as well but, if i want to edit the fiels of the Configurable Product like Name, Urlkey, Meta Title, Enable, Visibility, etc. These options are not updated at all, the only possible way to do this is by using Mass Actions "Change Status" and "Update Attributes"

My question is: What could be producing this behavior? Is there a possibility that the Configurable Product, is not saved, somehow, correctly in the catalog_product_entity-related tables?

Thanks.

Best Answer

I've managed to solve my problem by updating my MassConfigurable::assingProducts method in the following way:

private function assingProducts($productId, $products, $attributes) {
    $pr = $this->productFactory->create();
    $pr->load($productId);
    $pr->setTypeId('configurable');

    /** Associates selected Simple Products to newly created Configurable Product */
    $pr->setAssociatedProductIds($products);
    $pr->getTypeInstance()->setUsedProductAttributeIds($attributes,  $pr); //attributes IDs of product's attributes in my store
    $configurableAttributesData =  $pr->getTypeInstance()->getConfigurableAttributesAsArray($pr);
    $pr->setCanSaveConfigurableAttributes(true);
    $pr->setConfigurableAttributesData($configurableAttributesData);
    $pr->save();

    /** In the following bit of code, the Configurable Product gets duplicated and then it gets deleted.
     *  This leaves the duplicated product as the original one.
     */

    /** @var \Magento\Catalog\Model\Product $duplicate */
    $duplicate = $this->productFactory->create();
    $duplicate->setData($pr->getData());
    $duplicate->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED);
    $duplicate->setSku($pr->getSku());
    $duplicate->setUrlKey($pr->getUrlKey());
    $duplicate->setId($pr->getId());
    $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID);
    $pr->delete();
    $duplicate->save();
}

What i'm doing is that i load the Configurable Product that i created in createConfigurable() method, assing the simple products to it and then i save it. After that, i duplicate the Configurable Product, delete the original configurable product and save the duplicate in replace of the original.

I'm doing this because i discovered that the problem lies in updating a product programmatically (loading the product, adding or changing some of its attributes and saving it again). After updating the product this way, the product cannot be edited in Admin Panel. For the previous reason i have created another question because the problem exposed here is related but not caused directly by how the configurable product is created. This is the link of the question: How to correctly update product programmatically - Magento 2

Related Topic