3

I am trying to create some simple products, then have those simple products assigned to a configurable product programmatically. But no matter what I try, it seems to not associate them. What am I doing wrong?

Here is what I have so far. I have commented out all the attempts I have tried too so you can see what I've tried already:

namespace Company\Module\Controller\Get;

use Magento;
use Company\Module\Helper\Helper;

class OutboundItem extends Magento\Framework\App\Action\Action
{
    protected $storeManager;
    protected $productFactory;
    protected $indexerFactory;
    public $helper;

    public function __construct(
        Magento\Framework\App\Action\Context $context,
        Magento\Store\Model\StoreManagerInterface $storeManager,
        Magento\Catalog\Model\ProductFactory $productFactory,
        Magento\Indexer\Model\IndexerFactory $indexerFactory,
        Helper $helper
    )
    {
        $this->storeManager = $storeManager;
        $this->productFactory = $productFactory;
        $this->indexerFactory = $indexerFactory;
        $this->helper = $helper;
        return parent::__construct($context);
    }

    public function execute()
    {
        // Get Website ID
        $websiteId = $this->storeManager->getWebsite()->getWebsiteId();
        $storeId = $this->storeManager->getDefaultStoreView()->getId();

        // Get the the list of products that need importing from the document.
        $items = $this->helper->getItems();

        // Loop through all the results.
        foreach ($items as $item) {
            try {
                // Create common product info variables.
                $product_name = $item->ProdName;
                $price = $item->UnitPrice;
                $weight = $item->NetWeight;
                $categoryIds = [2, $this->helper->getCategoryIdByName(substr($product_name, 0, 1))];

                // Create variable to store the created variant ids.
                $variant_ids = array();

//                $prodData = array();
                // Loop through the variants.
                foreach ($item->variants as $variant) {
                    $product = $this->productFactory->create();

                    // Preparing data for new customer.
                    $product->setSku($product_name . '_' . $variant->Code);
                    $product->setName($variant->Description); // Name of Product
                    $product->setAttributeSetId($product->getDefaultAttributeSetId()); // Attribute set id
                    $product->setStatus(1); // Status on product enabled/ disabled 1/0
                    $product->setWeight($weight); // weight of product
                    $product->setVisibility(Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE); // visibilty of product (catalog / search / catalog, search / Not visible individually)
                    $product->setTypeId('simple'); // type of product (simple/virtual/downloadable/configurable)
                    $product->setPrice($variant->SalesPrice); // price of product
                    $product->setStockData(
                        array(
                            'use_config_manage_stock' => 0,
                            'manage_stock' => 1,
                            'is_in_stock' => 1,
                            'qty' => $variant->AvailableQuantity
                        )
                    );

                    $product->setWebsiteIds(array($websiteId));
                    $product->setCategoryIds($categoryIds);
                    $product->setStoreId($storeId);

                    $colour = $variant->value2;
                    $colourId = $this->helper->createOrGetId('color', $colour);
                    $product->setColor($colourId);

                    $size = $variant->value1;
                    $sizeId = $this->helper->createOrGetId('size', $size);
                    $product->setSize($sizeId);

                    // Save data.
                    $product->save();

                    if ($product->getId() > 0) {
                        $variant_ids[] = $product->getId();
//                        $prodData[$product->getId()] = array(
//                            '0' => array(
//                                'label' => 'Size',
//                                'attribute_id' => $this->helper->getAttribute('size')->getAttributeId(),
//                                'value_index' => $sizeId
//                            ),
//                            '1' => array(
//                                'label' => 'Color',
//                                'attribute_id' => $this->helper->getAttribute('color')->getAttributeId(),
//                                'value_index' => $colourId
//                            )
//                        );
                    }

                    $prodConfAttr = $product->getExtensionAttributes();
                    $prodConfAttr->setConfigurableProductLinks($variant_ids);
                    $product->setExtensionAttributes($prodConfAttr);
                }

                // Instantiate magento customer object.
                $product = $this->productFactory->create();

                // Preparing data for new customer.
                $product->setTypeId('configurable'); // type of product (simple/virtual/downloadable/configurable)
                $product->setSku($product_name);
                $product->setName($product_name); // Name of Product
                $product->setAttributeSetId($product->getDefaultAttributeSetId()); // Attribute set id
                $product->setStatus(1); // Status on product enabled/ disabled 1/0
                $product->setWeight($weight); // weight of product
                $product->setVisibility(Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH); // visibilty of product (catalog / search / catalog, search / Not visible individually)
                $product->setPrice($price); // price of product
                $product->setStockData(
                    array(
                        'use_config_manage_stock' => 0,
                        'manage_stock' => 1,
                        'is_in_stock' => 1
                    )
                );
                //setCategoryId?
                $product->setWebsiteIds(array($websiteId));
                $product->setCategoryIds($categoryIds);
                $product->setStoreId($storeId);

                // Set variants
//                $product->setNewVariationsAttributeSetId($product->getDefaultAttributeSetId());
//                $product->setAssociatedProductIds($variant_ids);
//                $product->setAffectConfigurableProductAttributes(4);
//
//                $product->getTypeInstance()->setUsedProductAttributeIds(array(
//                    $this->helper->getAttribute('color')->getAttributeId(),
//                    $this->helper->getAttribute('size')->getAttributeId()
//                ), $product);
//                $attData = $product->getTypeInstance()->getConfigurableAttributesAsArray();
//                $product->setCanSaveConfigurableAttributes(true);
//                $product->setConfigurableAttributesData($attData, $product);
////                $prodData = array();
////                foreach($variant_ids as $key => $id) {
////                    $prodData[$id] = array(
////                        '0' => array(
////                            'label'
////                        )
////                    );
////                }
//                $product->setConfigurableProductsData($prodData);

//                unset($variant_ids);

                // Save data.
                $product->save();

                echo $this->helper->getPrint($variant_ids);
                echo $this->helper->getPrint($categoryIds);
                echo $this->helper->getPrint($storeId);
                echo $this->helper->getPrint($item);
            } catch (\Exception $e) {
                echo $this->helper->getPrint($item);
                echo $this->helper->getPrint($e->getMessage());
            }
        }

        echo $this->helper->getPrint('Import Complete.');

        exit;
    }
}
1

I have done something similar for Grouped Products. I have created a simple product and linked it with a Grouped Product.

You should first load the following classes in your constructor.

public function __construct(
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
    \Magento\Catalog\Model\ProductLink\LinkFactory $productLinkFactory,
    \Magento\Catalog\Api\Data\ProductInterfaceFactory $productFactory
)
{
    $this->productRepository = $productRepository;
    $this->productLinkFactory = $productLinkFactory;
    $this->productFactory = $productFactory;
}

Then, you should create and save the simple product.

/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $this->productFactory->create();
$product->setSku('TEST_SKU');
$product->setName('TEST_NAME');
$product->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
$product->setTypeId('simple');
$product->setAttributeSetId(1);
$product->setPrice('10');
try {
    $product = $this->productRepository->save($product);
} catch (\Exception $e) {
    echo $e->getMessage();
    exit;
}

After that you can load your parent product, grouped or configurable.

// Load the parent product
/** @var \Magento\Catalog\Api\Data\ProductInterface $parentProduct */
$parentProduct = $this->productRepository->get('PARENT_SKU');

Finally, you can create product links as follows:

// Association with parent product
$link = $this->productLinkFactory->create()
    ->setSku($parentProduct->getSku())
    ->setLinkedProductSku($product->getSku())
    ->setLinkedProductType($product->getTypeId())
    ->setLinkType('associated');
$productLinks = $parentProduct->getProductLinks();
array_push($productLinks, $link);
$parentProduct->setProductLinks($productLinks);
try {
    $parentProduct = $this->productRepository->save($parentProduct);
} catch (\Exception $e) {
    echo $e->getMessage();
    exit;
}

I hope it works, I have not tested it for configurable products.

  • Thank you, I will try this and let you know how it goes :). – hazrpg Jul 18 '17 at 8:53
  • @hazrpg So, how did it go? – Bunyamin Inan Jul 19 '17 at 12:54
  • Sadly, it didn't work :( - the products still aren't linked. Is there anything I'm still doing wrong? I've converted from $link to array_push into an array before doing setProductLinks, since I thought that would work, but it doesn't. I haven't tried without doing that yet, but I will now. – hazrpg Jul 19 '17 at 22:43
1

Code should look like:

$product = $productFactory->create(['name'=> 'configurable product', ... ]);
$configurableOption = $optionFactory->create([]);
$linkedProduct = $linkFactory->create([]);
$product->getExtensionAttributes()->setConfigurableProductOptions($configurableOption);
$product->getExtensionAttributes()->setConfigurableProductLinks($linkedProduct);
$productRepository->save($product)

Please note that the API currently does not generate simple products, they need to be created in advance.

  • Thanks for your answer. When you say that simple products need to be created in advance - is the code in my example (found in the question) suitable enough for creating the simple products? E.g. can I create the simple products in a loop then attempt to link them after using your code? Is there anything special I need to include in the simple products? Could you please clarify what you mean. – hazrpg Aug 29 '17 at 12:47
0

None of the solutions here solved my problem - so I instead went about it a different way.

I used a CSV method - whereby the user downloads a CSV that was created in a similar fashion to the Export functionality provided by Magento 2. The user can then import that CSV using the import functionality.

The CSV is created using PHP fputcsv() function.

To avoid attribute issues (CSV imports don't work unless the attributes and options exist), I made sure they existed while making the CSV. Here is the functions I created for the job:

public function createOrGetId($attributeCode, $label) {
    if (strlen($label) < 1) {
        throw new Magento\Framework\Exception\LocalizedException(
            __('Label for %1 must not be empty.', $attributeCode)
        );
    }

    // Does it already exist?
    $optionId = $this->getOptionId($attributeCode, $label);

    if (!$optionId) {
        // If no, add it.

        /** @var \Magento\Eav\Model\Entity\Attribute\OptionLabel $optionLabel */
        $optionLabel = $this->optionLabelFactory->create();
        $optionLabel->setStoreId(0);
        $optionLabel->setLabel($label);

        $option = $this->optionFactory->create();
        $option->setLabel($optionLabel);
        $option->setStoreLabels([$optionLabel]);
        $option->setSortOrder(0);
        $option->setIsDefault(false);

        $this->attributeOptionManagement->add(
            Magento\Catalog\Model\Product::ENTITY,
            $this->getAttribute($attributeCode)->getAttributeId(),
            $option
        );

        // Get the inserted ID. Should be returned from the installer, but it isn't.
        $optionId = $this->getOptionId($attributeCode, $label, true);
    }

    return $optionId;
}

public function getOptionId($attributeCode, $label, $force = false) {
    /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
    $attribute = $this->getAttribute($attributeCode);

    // Build option array if necessary
    if ($force === true || !isset($this->attributeValues[ $attribute->getAttributeId() ])) {
        $this->attributeValues[ $attribute->getAttributeId() ] = [];

        // We have to generate a new sourceModel instance each time through to prevent it from
        // referencing its _options cache. No other way to get it to pick up newly-added values.

        /** @var \Magento\Eav\Model\Entity\Attribute\Source\Table $sourceModel */
        $sourceModel = $this->tableFactory->create();
        $sourceModel->setAttribute($attribute);

        foreach ($sourceModel->getAllOptions() as $option) {
            $this->attributeValues[ $attribute->getAttributeId() ][ $option['label'] ] = $option['value'];
        }
    }

    // Return option ID if exists
    if (isset($this->attributeValues[ $attribute->getAttributeId() ][ $label ])) {
        return $this->attributeValues[ $attribute->getAttributeId() ][ $label ];
    }

    // Return false if does not exist
    return false;
}

I still need to figure out how to automate the CSV imports, but at least this method avoids obvious (buggy?) limitations within the module framework.

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.