danneh.org

Creating products programmatically in Magento

by on May.22, 2012, under Magento, PHP, Software

Let’s face it – importing is hard. Especially if it’s from some other unfamiliar product, or something that doesn’t already have a Dataflow/import plugin for Magento already. Dataflow has its limits. I’ve actually found it easier to import manually (creating models for products) as opposed to writing Dataflow routines. However, one of my biggest bug bears is not just creating products in Magento programmatically, but creating them successfully. We always end up with stock or relationship issues between simple and configurable products.

I’ve spent the last few weeks importing from possibly the worst e-Commerce platform under the sun (it’s a custom built product that’s very dated). Getting the data out of it into something usable was hard enough, but then getting the data into Magento was another matter. I’d end up with missing (or duplicate) stock items, products appearing out of stock when the admin showed that they weren’t, missing images, missing simple-configurable relationships. It was all over the place. Eventually, I came up with something that seemed to work quite well. What I came up with is below:

First, create your simple products;

// There's some more advanced logic above the foreach loop which determines how to define $configurable_attribute,
// which is beyond the scope of this article. For reference purposes, I'm hard coding a value for
// $configurable_attribute here, and it's associated numerical attribute ID...
$configurable_attribute = "size"; $attr_id = 134;
$simpleProducts = array(); $lowestPrice = 999999;
 
// Loop through a pre-populated array of data gathered from the CSV files (or database) of old system..
foreach ($main_product_data['simple_products'] as $simple_product_data) {
    // Again, I have more logic to determine these fields, but for clarity, I'm still including the variables here hardcoded..
    $attr_value = $simple_product_data['size']; $attr_id = 134; 
 
    // We need the actual option ID of the attribute value ("XXL", "Large", etc..) so we can assign it to the product model later..
    // The code for getAttributeOptionValue and addAttributeOption is part of another article (linked below this code snippet)
    $configurableAttributeOptionId = getAttributeOptionValue($configurable_attribute, $attr_value);
    if (!$configurableAttributeOptionId) {
        $configurableAttributeOptionId = addAttributeOption($configurable_attribute, $attr_value);
    }
 
    // Create the Magento product model
    $sProduct = Mage::getModel('catalog/product');
    $sProduct
        ->setTypeId(Mage_Catalog_Model_Product_Type::TYPE_SIMPLE)
        ->setWebsiteIds(array(1))
        ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
        ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE)
        ->setTaxClassId(5)
        ->setAttributeSetId($_attributeSetMap[$data['product_type']]['attribute_set'])
        ->setCategoryIds($magento_categories) // Populated further up the script
        ->setSku($simple_product_data['sku'])
        // $main_product_data is an array created as part of a wider foreach loop, which this code is inside of
        ->setName($main_product_data['product_name'] . " - " . $attr_value)
        ->setShortDescription($main_product_data['short_description'])
        ->setDescription($main_product_data['long_description'])
        ->setPrice(sprintf("%0.2f", $simple_product_data['price']))
        ->setData($configurable_attribute, $configurableAttributeOptionId)
    ;
 
    // Set the stock data. Let Magento handle this as opposed to manually creating a cataloginventory/stock_item model..
    $sProduct->setStockData(array(
        'is_in_stock' =>; 1,
        'qty' => 99999
    ));
 
    $sProduct->save();
 
    // Store some data for later once we've created the configurable product, so we can
    // associate this simple product to it later..
    array_push(
        $simpleProducts,
        array(
            "id" => $sProduct->getId(),
            "price" => $sProduct->getPrice(),
            "attr_code" => $configurable_attribute,
            "attr_id" => $attr_id,
            "value" => $configurableAttributeOptionId,
            "label" => $attr_value
        )
    );
 
    if ($simple_product_data['price'] < $lowestPrice) {
        $lowestPrice = $simple_product_data['price'];
    }
}

Right now, we’ve got all the simple products added to Magento. The article I mentioned about getAttributeOptionValue and addAttributeOption is this article here.

Now, we create the configurable;

$cProduct = Mage::getModel('catalog/product');
$cProduct
    ->setTypeId(Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE)
    ->setTaxClassId(5)
    ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH)
    ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
    ->setWebsiteIds(array(1))
    ->setCategoryIds($magento_categories)
    ->setAttributeSetId(1) // You can determine this another way if you need to.
    ->setSku("C" . $main_product_data['product_ref_code'])
    ->setName($main_product_data['product_name'])
    ->setShortDescription(main_product_$data['short_description'])
    ->setDescription($main_product_data['long_description'])
    ->setPrice(sprintf("%0.2f", $lowestPrice))
    ->setUrlKey(getProductUrlKey($main_product_data['product_name']))
;

This is where things start to get a bit tricky…

$cProduct->setCanSaveConfigurableAttributes(true);
$cProduct->setCanSaveCustomOptions(true);
 
$cProductTypeInstance = $cProduct->getTypeInstance();
// This array is is an array of attribute ID's which the configurable product swings around (i.e; where you say when you
// create a configurable product in the admin area what attributes to use as options)
// $_attributeIds is an array which maps the attribute(s) used for configuration so their numerical counterparts.
// (there's probably a better way of doing this, but i was lazy, and it saved extra db calls);
// $_attributeIds = array("size" => 999, "color", => 1000, "material" => 1001); // etc..
 
$cProductTypeInstance->setUsedProductAttributeIds(array($_attributeIds[$configurable_attribute]));
 
// Now we need to get the information back in Magento's own format, and add bits of data to what it gives us..
$attributes_array = $cProductTypeInstance->getConfigurableAttributesAsArray();
foreach($attributes_array as $key => $attribute_array) {
    $attributes_array[$key]['use_default'] = 1;
    $attributes_array[$key]['position'] = 0;
 
    if (isset($attribute_array['frontend_label'])) {
        $attributes_array[$key]['label'] = $attribute_array['frontend_label'];
    }
    else {
        $attributes_array[$key]['label'] = $attribute_array['attribute_code'];
    }
}
 
// Add it back to the configurable product..
$cProduct->setConfigurableAttributesData($attributes_array);
 
// Remember that $simpleProducts array we created earlier? Now we need that data..
$dataArray = array();
foreach ($simpleProducts as $simpleArray) {
    $dataArray[$simpleArray['id']] = array();
    foreach ($attributes_array as $attrArray) {
        array_push(
            $dataArray[$simpleArray['id']],
            array(
                "attribute_id" => $simpleArray['attr_id'],
                "label" => $simpleArray['label'],
                "is_percent" => false,
                "pricing_value" => $simpleArray['price']
            )
        );
    }
}
 
// This tells Magento to associate the given simple products to this configurable product..
$cProduct->setConfigurableProductsData($dataArray);
 
// Set stock data. Yes, it needs stock data. No qty, but we need to tell it to manage stock, and that it's actually
// in stock, else we'll end up with problems later..
$cProduct->setStockData(array(
    'use_config_manage_stock' => 1,
    'is_in_stock' => 1,
    'is_salable' => 1
));
 
// Finally...!
$cProduct->save();

At this point, go back and check in your Magento admin area for the products. Your product should exist, have simple products associated with it and, more importantly, appear on the frontend with the relevant options attached to it to purchase. Adding images programmatically is just as simple. Suppose you have an array of image files on the filesystem that you need to associate to this product;

$count = 0;
foreach ($listOfImages as $imagePath) {
    $mode = array();
    if ($count == 0) {
        $mode = array("thumbnail", "small_image", "image");
    }
    $cProduct->addImageToMediaGallery($imagePath, $mode, false, false);
}
 
// For good measure;
$cProduct->save();

14 Comments for this entry

  • starjahid

    Hi

    I can not create Quick simple product programmatically.

    Can any one help me?

  • Roberto

    Do you have idea what I need to to to add New product from date value?

    I tried
    $product->setNewsFromDate(strtotime(‘now’));

    and

    $product->setNewFromDate(strtotime(‘now’));

    but no luck.
    I couldnt manage stock either

    $cProduct->setStockData(array(
    ‘use_config_manage_stock’ => 1,
    ‘is_in_stock’ => 1,
    ‘is_salable’ => 1
    ));

  • Ramazan

    Hi, thanks a lot for this snippet.
    But while creating configurable, you used an array named $_attributeIds, bu it never assigned before.
    Some confuse :/

    may you help ?

  • Muhammed

    Nice tutorial man

    First it worked for me,
    then I made little changes and now I am getting this error

    Fatal error: Call to a member function setStoreId() on a non-object in /home/**/domains/***/public_html/site/app/code/core/Mage/Catalog/Model/Abstract.php on line 212

    in my code I have never used the setStoreId() function

  • Dan

    @Ramazan; thanks for pointing that out. That’s left-over code from the actual import script I was doing. I included an explanation above that line as to what that array is, and how it’s setup.

    @Muhammed; This is originating from the core where it’s trying to get a product collection and set a store id filter on it before it returns. The only thing I can think of is that there’s a module installed which is incorrectly overriding the catalog/product resource models. If nothing’s changed from your installation point of view, and it’s a change you made in the importer script, you should try and find out where your import script is failing, i.e; what call are you making in your script which triggers this error. Then backtrack from that, making sure everything else up to that point is as it should be, and the data being passed around is valid and in the correct format.

  • Muhammed

    Thanks for the reply, I will start over with a fresh install of magento.

    But I still got some questions. What if I have 3 options for every product.

    You have hard coded one configurable attribute (“size”)

    I have size, properties, color.. it has to be in an array I suppose, but the array structure in magento scares me :( , an array in an array which is in an array etc..

    do you have any idea?

  • Chuck

    @roberto:
    I know it’s been a couple of months and you’ve probably stopped caring by now, but have you tried using $cproduct->setData(‘news_from_date’, strtotime(‘now’));
    I find that setData is always a good fall-back when nothing else seems to work :)

  • Lauren

    Hi

    I am having some integrity issues, super attribute label table is empty so what should i do?

    exception: exception ‘PDOException’ with message ‘SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`b_magento`.`catalog_product_super_attribute_label`, CONSTRAINT `FK_309442281DF7784210ED82B2CC51E5D5` FOREIGN KEY (`product_super_attribute_id`) REFERENCES `catalog_product_super_attribut)’ in /var/www/public_html/lib/Zend/Db/Statement/Pdo.php:228
    Stack trace:

    can you please help me

  • Lauren

    I have solved this problem, but now Im having troubles with the STOCK Management, I keep getting Out of stock for every product
    I don’t think this code works

    $cProduct->setStockData(array( ‘use_config_manage_stock’ => 1, ‘is_in_stock’ => 1, ‘is_salable’ => 1 ));

    Read more at http://www.danneh.org/2012/05/creating-products-programmatically-magento/#ixzz26MyBaKC7

    I tried everything…. any ideas?

  • Dan

    @Lauren;
    Re-index. That is all. For some reason, this code doesn’t update the is-in-stock indexes (which I suppose for one is a good thing, because the import happens quicker), but did confuse me for a long while, until I tried re-indexing everything, then everything came into stock!

  • John

    I have created some custom php code which builds my entire magento website on the fly based on a product set from any website I choose. http://www.computer-desks-uk.co.uk – my question is, because the products don’t physically exist in the back end is there a way of adding them to the cart without sending the visitor to the top level website where I pilphered the products from in the first instance?

    Thanks!

  • Muhammed

    Okay I found out how to fix the stock data

    I edited the function isSalable() somewhere in the magento core so it returns true ‘hardcoded’

    And You have to use stock data model like product model etc

    $stockItem = Mage::getModel(‘cataloginventory/stock_item’);
    $stockItem->assignProduct($existingConfiruableProduct);
    $stockItem->setData(‘is_in_stock’, 1);
    $stockItem->setData(‘stock_id’, 1);
    $stockItem->setData(‘store_id’, 1);
    $stockItem->setData(‘manage_stock’, 1);
    $stockItem->setData(‘use_config_manage_stock’, 0);
    $stockItem->save();

  • Stefan

    Hi,

    Is there a short and simple method to set a specific attribute value to an product object?
    I’m looking for something like this: $product->setAttribute( ‘attributeName’ , ‘myValue’ )

  • Stefan

    How is it possible to set all values (SKU, Price, Status, etc) on the WebsiteId “Layer”, but “Name” and “ShortDescription” not? “Name” and “ShortDescription” should be set on storeview scope, but I didn’t get it to work:

    $product->setStoreId( $store->getId() )->setData( “name” , $my_name );

1 Trackback or Pingback for this entry

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Blogroll

A few highly recommended websites...