3

I have a php script in magento that programmatically creates products in a for each loop as below:

$i = 0;
foreach ($array as $product) {
    createProduct($params);
    $i++;
    if($i == $count){
        // finish and continue
    }
}

function createProduct($params){
    $product = new Mage_Catalog_Model_Product();
    $product->setCreatedAt(strtotime('now'));
    $product->setAttributeSetId(11);
    $product->setName(xxxxxx);
    $product->setPrice(xxxxx);
    $product->setDescription(xxxxxxx);  
    $product->setCategoryIds(array(xx,xxx));
    // and so on...

    try {
        $product->save();
    }
    catch (Exception $ex) {
        // catch error
    }

With each product i need to be able to set the category product position (see below)

How can this be done? I want to use the $i in the for each loop to be the position

enter image description here

4
  • Will there be any products in the category at the point you run the script? Commented Jan 13, 2015 at 18:08
  • No the category will be empty when the script runs
    – odd_duck
    Commented Jan 13, 2015 at 18:17
  • Added a quick example assuming you would say there will be. If you want a solution using the models take a look at Mage_Catalog_Model_Resource_Category::_saveCategoryProducts. Commented Jan 13, 2015 at 18:23
  • Please take a look at my answer, Magento provides an API method specifically for what you're looking for. Commented Jan 13, 2015 at 19:13

2 Answers 2

4

There is an API class with a method specifically for this. In version 1.9.1.0 you can find the function defined in app/code/core/Mage/Catalog/Model/Category/Api.php line 467

public function assignProduct($categoryId, $productId, $position = null, $identifierType = null)

So, my recommendation is to scrap using $product->setCategoryIds() and utilize this method instead. $product->setCategoryIds() works just fine if you know ALL the categories the product should belong too, and don't care about position. However it can easily cause problems for you especially if you're doing scripting work on existing products, as it will blow out any other categories the product may have been assigned to (that you didn't supply the id for) as well as blow out the current position the product has been assigned even if the product was already associated to the category prior to performing your scripting work.

Anyways... so if we examine the method's definition, we can see that we need to already have a product id. So if we were to reference your original code, you'll want to save the product, then associate it to the category/categories.

$i = 0;
foreach ($array as $product) {
    createProduct($params, $i);
    $i++;
    if($i == $count){
        // finish and continue
    }
}

function createProduct($params, $position)
{
    $product = Mage::getModel('catalog/product');

    $product->setCreatedAt(strtotime('now'));
    $product->setAttributeSetId(11);
    $product->setName(xxxxxx);
    $product->setPrice(xxxxx);
    $product->setDescription(xxxxxxx);  

    // and so on...

    $category_ids = array('xx','xxx');

    try {
        $product->save();
        $api = Mage::getSingleton('catalog/category_api');
        foreach ($category_ids as $category_id) {
            $api->assignProduct($category_id, $product->getId(), $position);
        }
    }
    catch (Exception $ex) {
        // catch error
    }
}
3
  • spot on, thank you for the help and the in depth explanation
    – odd_duck
    Commented Jan 15, 2015 at 13:36
  • Do you know how make this nice solution working with Catalog Flat tables enabled? By default throws an error. Commented Dec 3, 2015 at 16:33
  • @eliosolutions can you provide any additional information on the error thrown? Commented Dec 4, 2015 at 17:30
3

The simplest way would probably be directly via SQL, you should be able to do something along the lines of:

$table = $this->getTable('catalog/category_product');
$conn = Mage::getModel('core/resource')->getConnection('core_write');

try {
    $product->save();
    foreach ($categories as $categoryId) {
        $productOrderData = array(
            'product_id'  => (int)$product->getId(),
            'category_id' => (int)$categoryId
            'position'    => (int)$i
        );

        $conn->insert($table, $productOrderData);
    }
}

Obviously you'll need to pass $i into the function if you wish to use that for the position.

4
  • There is an API method specifically for this, please see my answer. Commented Jan 13, 2015 at 19:12
  • 1
    Nice, I've had very little exposure to the API, good to know. Commented Jan 13, 2015 at 20:54
  • 2
    I stumbled upon this post again randomly and it's interesting to see how things change. These days I would probably use your solution over the API that I had recommended as it eliminates a lot of the overhead that Magento's save methods often have. Especially with large data sets I more and more resort to straight SQL queries (keeping in mind however, that any event observers are lost using such an approach). Anyhow.. +1 for direct SQL :) Commented Jun 14, 2016 at 15:16
  • Mage::getSingleton('catalog/category_api')->assignProduct is truly insane in terms of resource/time consumption. As soon as you have a couple of thousands products of which you want to adjust the position regularly you really should go for this SQL based approach.
    – Jey DWork
    Commented Mar 15, 2017 at 16:40

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.