I am trying to figure out the behaviour I'm getting when saving custom product options programmatically.

I am returning a collection of products ($simples) then:

foreach ($simples as $simple)
    $custom_options = array($option_one, $option_two);

    $product = Mage::getModel('catalog/product')->load($simple->getId());



$custom_options is simply an array of two option arrays:

[0] => Array
        [title] => Option One
        [sku] => OPTIONONE
        [type] => field
        [price] => 0
        [is_require] => 0
        [sort_order] => 0
        [max_characters] => 100
[1] => Array
        [title] => Option Two
        [sku] => OPTIONTWO
        [type] => field
        [price] => 0
        [is_require] => 0
        [sort_order] => 0
        [max_characters] => 100

However the iteration value appears to have some kind of effect on save(), as for each increase, each of the arrays above are added to the product that many times i.e.

Product from loop 1 has: Array[0], Array[1]
Product from loop 2 has: Array[0], Array[0], Array[1], Array[1]
Product from loop 3 has: Array[0], Array[0], Array[0], Array[1], Array[1], Array[1]

I can see that in \Mage\Catalog\Model\Resource\Product\Option.php

protected function _afterSave(Mage_Core_Model_Abstract $object)

is called multiple times on each array, I just need to understand why.

Edit: I have just created an observer and logged the calls, this is what I get:

2013-09-15T13:49:12+00:00 INFO (6): Calling save() on product: 74
2013-09-15T13:49:12+00:00 INFO (6): PRODUCT SAVE BEFORE CALLED: 74
2013-09-15T13:49:12+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:12+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:12+00:00 INFO (6): PRODUCT SAVE AFTER CALLED: 74
2013-09-15T13:49:13+00:00 INFO (6): SAVE SUCCESS FOR PRODUCT: 74
2013-09-15T13:49:13+00:00 INFO (6): Calling save() on product: 75
2013-09-15T13:49:13+00:00 INFO (6): PRODUCT SAVE BEFORE CALLED: 75
2013-09-15T13:49:13+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:13+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:13+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:13+00:00 INFO (6): Product option _afterSave() called...
2013-09-15T13:49:13+00:00 INFO (6): PRODUCT SAVE AFTER CALLED: 75
2013-09-15T13:49:14+00:00 INFO (6): SAVE SUCCESS FOR PRODUCT: 75

As you can see, the products save() method is only called once, but the options are being duplicated for each loop iteration

  • Place a debug_backtrace (ref: php.net/manual/en/function.debug-backtrace.php ) in the _afterSave method. This will help get the stack trace of program execution to that point, and from that you can see where this is originating from. better yet is to get xdebug installed, and then use breakpoints, which will be a lot easier to help determine application flow. – ProxiBlue Sep 15 '13 at 14:35
  • Hi Proxi, using debug_backtrace nearly killed my browser! The site itself is on an internal server and not local, would step-through still be possible? – Invoke Sep 18 '13 at 12:36
  • Hi, yes. xdebug can do remote debugging. see: xdebug.org/docs/remote. In case it helps, this is my xdebug ini for php: zend_extension=/usr/lib/php5/20090626/xdebug.so xdebug.remote_enable=1 xdebug.remote_handler=dbgp xdebug.remote_mode=req xdebug.remote_host= xdebug.remote_port=9000 ;xdebug.remote_autostart=1 ;xdebug.remote_mode=jit xdebug.profiler_enable=0 xdebug.profiler_output_dir=/tmp/ xdebug.profiler_enable_trigger=1 – ProxiBlue Sep 19 '13 at 1:47

OK so I finally came back to this issue and the following post helped me figure this one out: https://stackoverflow.com/questions/4006260/magento-accumulating-custom-options-in-script

As the user in the post above says, the Magento product option model was 'designed with a singleton pattern', which means for each loop I am adding the product options again, then setting these duplicated options to the following product.

So the fix is simply to call Mage::getSingleton('catalog/product_option')->unsetOptions(); before we call setProductOptions. There is no need to call load() either, the model returned in the collection is adequate.

So yeah, simple when you know how eh! I was really tearing my hair out on this one, thanks Magento!

  • 1
    This saved me after days trying to resolve – odd_duck Sep 19 '14 at 8:29
  • Dear Invoke, when I read your answer I was so happy to finally found someone who encountered exactly the same issue as me. But my problem seems a bit tricky. When I run my script by CLI or starting "manually" by cron (by AOE Scheduler), the custom options are created as expected. The problem happens when my module script is started AUTOMATICALLY by cron. Do you have an idea why starting by cron can cause a duplicate custom options issue? – DarkCowboy Dec 10 '15 at 14:10
  • Great answer! Was having the exact same problem - I was looping through 1000+ SKU's and by the time I got 30 or so done, it was spamming the product with more and more iterations! – Thomas Harding Dec 18 '17 at 16:16

Try calling reset on the $product object after saving to reset all public data that you've added to the object.

  • Thanks for the reply Sander, I actually tried setting the $product object to null after each save but it has no effect, reset is the same unfortunately – Invoke Sep 18 '13 at 12:31

This is working perfect.

foreach($productIds as $id){ 

 $product = Mage::getModel('catalog/product')->load($id);
 try {
       if(!$product->getOptionsReadonly()) {
      } catch (Exception $e) {


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.