2

I have a configurable product and am trying to programmatically create a new simple product and then associate it with my configurable product. This is what I have so far...

$newProduct = $this->_productFactory->create();
$newProduct->setName('Test Product')
    ->setTypeId(Type::TYPE_SIMPLE)
    ->setSku('test-product')
    ->setPrice($configurableProduct->getPrice())
    ->setWeight($configurableProduct->getWeight())
    ->setStatus(Status::STATUS_ENABLED)
    ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
    ->setAttributeSetId($configurableProduct->getAttributeSetId())
    ->setData('my_product_attribute', 'sample_value');
$this->_productRepository->save($newProduct);
$newId = $newProduct->getId();

However, when I try to get the new product ID $newId after using the product repository to save, the value is null. The simple product saves correctly, but I need the ID to be able to associate it with the configurable one. Am I missing something?

1 Answer 1

6

Okay, here's the situation: Because of how the product save works, $this->_productRepository->save($newProduct); does not reflect changes back onto your $newProduct. It's actually creating a new Product instance and copying your data onto it inside there. Sound slow and inefficient? Yeah. This approach probably makes data integrity easier to maintain though.

To walk through that core method:

  1. You pass a Product into the method, used as $product.

    public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveOptions = false)
    
  2. They re-load the product by SKU, and copy link data from it onto yours.

        $existingProduct = $this->get($product->getSku());
        $product->setData(
            $this->resourceModel->getLinkField(),
            $existingProduct->getData($this->resourceModel->getLinkField())
        );
    
  3. They convert your product into a data array.

    $productDataArray = $this->extensibleDataObjectConverter
        ->toNestedArray($product, [], \Magento\Catalog\Api\Data\ProductInterface::class);
    $productDataArray = array_replace($productDataArray, $product->getData());
    
  4. They create a new Product object from that data.

    $product = $this->initializeProductData($productDataArray, empty($existingProduct));
    
  5. They save that new $product.

        unset($this->instances[$product->getSku()]);
        unset($this->instancesById[$product->getId()]);
        $this->resourceModel->save($product);
    
  6. Finally, they load the product again, and return that.

    return $this->get($product->getSku());
    

What this means in practice: $newProduct (the Product you passed in) will not give you any data about the save. It's exactly the same object after saving as you had before, because it's not touched directly (except for in #2). But $this->_productRepository->save() actually returns the saved product model. Basically, they're shuffling objects around, and that complicates things.

With all that in mind, try this for your product save:

$newProduct = $this->_productFactory->create();
$newProduct->setName('Test Product')
    ->setTypeId(Type::TYPE_SIMPLE)
    ->setSku('test-product')
    ->setPrice($configurableProduct->getPrice())
    ->setWeight($configurableProduct->getWeight())
    ->setStatus(Status::STATUS_ENABLED)
    ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
    ->setAttributeSetId($configurableProduct->getAttributeSetId())
    ->setData('my_product_attribute', 'sample_value');
$newProduct = $this->_productRepository->save($newProduct);
$newId = $newProduct->getId();

Note that we're reassigning $newProduct to the result of the repository save. That gives us the saved ID and anything else that resulted.

1
  • Ah, fantastic... thank you for explaining this so clearly! May 2, 2017 at 3:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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