Question

I have created this custom import module that will create products and set different values for each store view. The problem is, when I save a single attribute for the product on a store view, all the other attributes are saved to it as well for that store view. I want the store view version to inherit (use default) all values except the one I'm changing (description).

This is stripped/simplified version of the script:

// Create a product model using \Magento\Catalog\Model\ProductFactory
$productModel = $this->productFactory->create();

// Set the default values
$productModel
  ->setStoreId(0)
  ->setName($name)
  ->setAttributeSetId($productModel->getDefaultAttributeSetId())
  ->setWebsiteIds([1,2])
  ->setVisibility(4)
  ->setUrlKey($urlKey)
  ->setStatus(2)
  ->setTypeId('simple')
  ->setPrice(10)
  ->save();

// Reload product
$productStoreView = $this->productFactory->create()
  ->load($productModel->getId())
  ->setStoreId(1)
  ->setDescription('Desc for store view 1')
  ->save();

$productStoreView = $this->productFactory->create()
  ->load($productModel->getId())
  ->setStoreId(2)
  ->setDescription('Desc for store view 2')
  ->save();

The result now when I, using Magento 2 admin, looking at the product, each store view has it's own values for status, name etc set for each store view when it should only be "description" that isn't using default value.

Any tips on what I'm doing wrong?

Updated 2017-01-30:

I've started up a brand new environment with a fresh Magento 2.1.3 install. Then I created a new website, store and store view with the same root catalog.

After that, I saved this script in pub/test.php and executed it via CLI (I know it's not best practice, it's just to test/prove my case).

I visit admin and click on the newly created product. The default view looks as it's supposed to. I then switch stores and status is not using default value among other attributes.

<?php

use Magento\Framework\App\Bootstrap;

require __DIR__ . '/../app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);

$obj = $bootstrap->getObjectManager();

$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');

$product = $obj->get('Magento\Catalog\Model\Product');

$product
    ->setName('Random name ' . time())
    ->setSku(time())
    ->setPrice(0)
    ->setStatus(2)// Disabled
    ->setWebsiteIds([1, 2])
    ->setVisibility(4)
    ->setAttributeSetId($product->getDefaultAttributeSetId())
    ->setTypeId('simple')
    ->save();

echo "Saved product ID " . $product->getId() . "\n";

$obj->create('Magento\Catalog\Model\Product')
    ->load($product->getId())
    ->setStoreId(1)
    ->setDescription('Description store view 1')
    ->save();

$obj->create('Magento\Catalog\Model\Product')
    ->load($product->getId())
    ->setStoreId(2)
    ->setDescription('Description store view 2')
    ->save();

All store views: https://i.stack.imgur.com/YkGjV.jpg

Store view 1: https://i.stack.imgur.com/C8Tuh.jpg

Store view 2: https://i.stack.imgur.com/uAS2h.jpg

Was it helpful?

Solution 2

As of today no fix has been released by Magento and there are several others having similar issues. There are also many issues on Github describing the same (or similar) problems.

My only work-around right now is to "reset" the fields that M2 feels the need to update.

$this->objectManager->create('Magento\Catalog\Model\Product')
    ->load($product->getId())
    ->setStoreId($storeviewId)
    ->setStatus($storeStatus)
    ->setDescription('Store description')
    ->setName(null) // Use default
    ->setPrice($price) // Store price
    ->setTaxClassId(null) // Use default
    ->setVisibility(null) // Use default
    ->setCountryOfManufacture(null) // Use default
    ->save();

OTHER TIPS

I think the best answer to this question currently looks like this (using price as example attribute):

/** @var  \Magento\Catalog\Model\ProductFactory */
protected $productFactory;

/** @var  \Magento\Catalog\Model\ResourceModel\Product */
protected $productResourceModel;

public function __construct(
    \Magento\Catalog\Model\ProductFactory $productFactory,
    \Magento\Catalog\Model\ResourceModel\Product $productResourceModel
)
{
    $this->productFactory = $productFactory;
    $this->productResourceModel = $productResourceModel;
}


private function setStoreViewPrice($productId, $storeId, $price)
{
    $product = $this->productFactory->create();
    $this->productResourceModel->load($product, $productId);
    $product->setStoreId($storeId);
    $product->setPrice($price);
    $this->productResourceModel->saveAttribute($product, 'price');
}

See also https://magento.stackexchange.com/a/114958/35180

// Reload product

$productStoreView = $this->productFactory->create()
  ->load($productModel->getId());

 $productStoreView->setDescription('Desc for store view 1');
 $productStoreView->setStoreId(1);
 $productStoreView->save();

I think you have to set storeId after loading product object. I have tried in my system and it is working fine.

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $objectManager->create('Magento\Catalog\Model\Product')->load(2)->setStoreId(1)->setDescription('english content')->save();
        $objectManager->create('Magento\Catalog\Model\Product')->load(2)->setStoreId(2)->setDescription('french content')->save();

I know direct loading of object manager is not good but I have created simple script to check is it working or not.

In oder to set different store specific attribute values for a product in different stores of a Magento2 installation, the class \Magento\Catalog\Model\ResourceModel\Product\Action::class can be used:

/**
 * Sets different values for an attribute, depending on the store.
 * 
 * This example sets the hs code attribute of product 123 to 111111 for the UK and 222222 for AU.
 */
public function testSetProductAttributes()
{
    $storeIdUK = 1;
    $storeIdAU = 2;

    $product_id = 123;

    $hs_code_uk = '111111';
    $hs_code_au = '222222';

    $attrDataUK = [
      'hs_code' => $hs_code_uk
    ];
    $attrDataAU = [
      'hs_code' => $hs_code_au
    ];

    $productAction = $this->_objectManager->get(\Magento\Catalog\Model\ResourceModel\Product\Action::class);

    $productAction->updateAttributes([$product_id], $attrDataUK, $storeIdUK);
    $productAction->updateAttributes([$product_id], $attrDataAU, $storeIdAU);
}

This works for me in Magento 2.2.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top