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

  • Will there be any products in the category at the point you run the script? – Peter O'Callaghan Jan 13 '15 at 18:08
  • No the category will be empty when the script runs – odd_duck Jan 13 '15 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. – Peter O'Callaghan Jan 13 '15 at 18:23
  • Please take a look at my answer, Magento provides an API method specifically for what you're looking for. – Darren Felton Jan 13 '15 at 19:13
3

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
    }
}
  • spot on, thank you for the help and the in depth explanation – odd_duck Jan 15 '15 at 13:36
  • Do you know how make this nice solution working with Catalog Flat tables enabled? By default throws an error. – Elio Ermini Dec 3 '15 at 16:33
  • @eliosolutions can you provide any additional information on the error thrown? – Darren Felton Dec 4 '15 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.

  • There is an API method specifically for this, please see my answer. – Darren Felton Jan 13 '15 at 19:12
  • 1
    Nice, I've had very little exposure to the API, good to know. – Peter O'Callaghan Jan 13 '15 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 :) – Darren Felton Jun 14 '16 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 Mar 15 '17 at 16:40

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.