Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

Adding Custom Options to Products in Magento

In my post A Magento File Custom Option Type Primer I’ve talked about how file custom product options are handled in Magento. One of the readers posted a comment there asking if there was an alternative to manually adding custom options to existing products. For such a task I can recommend MAGMI, Magento Mass Importer, which was used in one of my projects to import product data including custom options. Another solution would be writing a script which could dynamically add custom options to a set of existing products. In this post I will show you how to create such script.

First, let’s see how custom option data are written to database when a product is saved. The product model class Mage_Catalog_Model_Product has a protected method _afterSave which is called, as the name suggests, after a product model is saved.

protected function _afterSave()
{
    $this->getLinkInstance()->saveProductRelations($this);
    $this->getTypeInstance(true)->save($this);

    /**
     * Product Options
     */
    $this->getOptionInstance()->setProduct($this)
        ->saveOptions();

    $result = parent::_afterSave();

    Mage::getSingleton('index/indexer')->processEntityAction(
        $this, self::ENTITY, Mage_Index_Model_Event::TYPE_SAVE
    );
    return $result;
}

Listing 1. Saving custom options of a product in an after save routine, /app/code/core/Mage/Catalog/Model/Product.php, line 537.

The line we are interested in is $this->getOptionInstance()->setProduct($this)->saveOptions();. Here, a product instantiates an object of type Mage_Catalog_Model_Product_Option. The option instance receives the product object and then calls its method saveOptions(). This method iterates through elements of an internal _options array, connects them to the product object, and saves them. The options data are then written to table catalog_product_option. Our script is going to do the same. First, we start the Magento app and define a set of products to which we are going to add a custom option (in my example it is an array with one element, ID 180).

<?php 
require_once 'app/Mage.php';
Mage::app('default');

$productIds = array(180);

Listing 2. Initializing the Magento application.

I am passing the “default” shop name to the app. If you are using multiple stores, make sure you start the app with a proper store. The next step is to define an option that we are going to add to our products. The option is an array, and its elements are properties you would normally set up manually in the back-end.

$option = array(
        'title' => 'Test Option',
        'type' => 'file',
        'is_require' => 1,
        'price' => 10,
        'price_type' => 'fixed',
        'sku' => 'testsku',
        'file_extension' => 'png,jpg',
        'image_size_x' => '100',
        'image_size_y' => '200'
);

Listing 3. Properties of an option.

The available properties are as follows:

  • title – the option’s label;
  • type – option type, possible values:
    • field – simple input text,
    • area – multiline text area,
    • file – file with an upload form in the front-end,
    • drop_down – drop-down list with predefined values,
    • radio – a set of radio buttons,
    • checkbox – multiple selectable checkbox options,
    • multiselect – list of multiple selectable options,
    • date – date input field,
    • date_time – date and time combined,
    • time – time only;
  • is_require – a flag indicating if the option is required, 1 or 0;
  • price – price surcharge for the option;
  • price_type – type of the option surcharge, fixed or percent (of the product’s price);
  • sku – SKU for the option.

Some of the properties are type-dependent, e.g., a “file” option also has:

  • file_extension – a string of comma-separated file extensions that are accepted by the option; if empty any extension except “exe” and “php” is allowed;
  • image_size_x and image_size_y – image dimension limits in pixels;

An option of type “field” can have the following property:

  • max_characters – maximum allowed number of characters.

The final step of the script is a loop in which we add this option to every product from the $productIds array:

foreach ($productIds as $productId) {
        $product = Mage::getModel('catalog/product')->load($productId);
        $optionInstance = $product->getOptionInstance()->unsetOptions();

        $product->setHasOptions(1);
        if (isset($option['is_require']) && ($option['is_require'] == 1)) {
                $product->setRequiredOptions(1);
        }
        $optionInstance->addOption($option);
        $optionInstance->setProduct($product);
        $product->save();

}

Listing 4. Adding the option to products.

Note that I set value 1 to product property has_options, and if the custom option is required, also to required_options. This way I let the system know that the given product has options and that they are required (if they are required, of course). Saving the product also saves the custom option.

The script can be found here: Dynamically adding a file type custom option to multiple products in Magento.

Readers who read this post also read these:

16 thoughts on “Adding Custom Options to Products in Magento

  1. I am trying to use this to create a radio button attribute. I am getting an error saying that “Select type options required values rows.”. Here is my array:

    $option = array(
    ‘title’ => $title,
    ‘type’ => ‘radio’,
    ‘is_require’ => 1,
    ‘price’ => null,
    ‘price_type’ => null,
    ‘sku’ => ‘testsku’,
    ‘file_extension’ => null,
    ‘image_size_x’ => ‘0’,
    ‘image_size_y’ => ‘0’,
    ‘options’ => array(
    ‘optionName’ => ‘optionValue’,
    ‘optionName2’ => ‘optionValue2’)
    );

  2. Hi Martin,

    If you are trying to create a custom option of type “select” (Drop-down), you must provide an array element “values” which consists of option values, each of them an array. The option values array must contain at least these elements:

               "title" => option title
                "price" => option price
                "price_type" => option price type ("fixed" or "percent")
                "sku" => option SKU
    

    This is an example of a “values” array:

      "values" => array(
        "0" => array(
            "option_type_id" => -1,
            "is_delete" => '',
            "title" => "t1",
            "price" => "1",
            "price_type" => "fixed",
            "sku" => "111",
            "sort_order" => ''
          ),  
        "1" => array(
            "option_type_id" => -1,
            "is_delete" => '',
            "title" => "t2",
            "price" => "2",
            "price_type" => "fixed",
            "sku" => "222",
            "sort_order" => '' 
          )
    );
    

    Elements “option_type_id”, if present, must be set to -1.

  3. Thank you! Finally a method that really works. I was always having trouble getting the products has_options field to be stored correctly. But it works with your Method

  4. Hi, can i add more custom options to product cuz with code above i can not do it. Only last one is saved…

    $options = array(
        array(
            'title' => 'First Line',
            'type' => 'text',
            'is_require' => 0,
            'sort_order' => 1,
        ),
        array(
            'title' => 'Second Line',
            'type' => 'text',
            'is_require' => 0,
            'sort_order' => 2,
        ),
    );
    
    foreach($options as $option){
    
        $optionInstance = $product->getOptionInstance()->unsetOptions();
    
        $product->setHasOptions(1);
        if (isset($option['is_require']) && ($option['is_require'] == 1)) {
            $product->setRequiredOptions(1);
        }
        $optionInstance->addOption($option);
        $optionInstance->setProduct($product);
        $product->save();
    }
    
    • Hi,

      The main problem in your code is that the option type is wrong: it should be field instead of text. Because of the wrong type the options can’t be correctly saved.
      Also it would be better to change the loop to add all options to $optionInstance variable and save the product once instead of each time for every option.

      The resulting code that works in my shop is this:

      $options = array(
          array(
              'title' => 'First Line',
              'type' => 'field',
              'is_require' => 0,
              'sort_order' => 1
          ),
          array(
              'title' => 'Second Line',
              'type' => 'field',
              'is_require' => 0,
              'sort_order' => 2
          ),
      );
      
      $product = Mage::getModel('catalog/product')->load($productId);
      $optionInstance = $product->getOptionInstance()->unsetOptions();
      
      $product->setHasOptions(1);
      
      foreach($options as $option){
          if (isset($option['is_require']) && ($option['is_require'] == 1)) {
              $product->setRequiredOptions(1);
          }
          $optionInstance->addOption($option);
      }
      $optionInstance->setProduct($product);
      $product->save();
      
  5. for those who receive the Integrity constraint violation: 1062 Duplicate entry ….

    istead of $product->save()

    add saveOptions();
    in :
    $optionInstance->setProduct($product)->saveOptions();

  6. Hi,

    Thank you very much for this.

    For some reason (CE 1.7) my values are not being added. My script is almost identical to yours:


    'Test',
    'type' => 'drop_down',
    'is_require' => 1,
    'sort_order' => 0,
    'values' => array (
    array (
    'title' => 'Option Value 1',
    'price' =>100,
    'price_type' => 'fixed',
    'sku' => 'any sku for 1',
    'sort_order' => '1'
    ),
    array (
    'title' => 'Option Value 2',
    'price' =>100,
    'price_type' => 'fixed',
    'sku' => 'any sku for 2',
    'sort_order' => '1'
    ),
    array (
    'title' => 'Option Value 3',
    'price' =>100,
    'price_type' => 'fixed',
    'sku' => 'any sku for 3',
    'sort_order' => '1'
    )
    )
    );

    foreach ($productIds as $productId) {
    $product = Mage::getModel('catalog/product')->load($productId);
    $optionInstance = $product->getOptionInstance()->unsetOptions();
    $product->setHasOptions(1);
    if (isset($option['is_require']) && ($option['is_require'] == 1)) {
    $product->setRequiredOptions(1);
    }
    $optionInstance->addOption($option);
    $optionInstance->setProduct($product);
    $product->save();
    }

    Any ideas?

  7. Absolutely cannot get this to work when using select option types while observing `catalog_controller_product_view`.

    Like others, my code is identical and I’m using 1.9x

  8. Hello I don’t want to give custome option to every product i want to give it to product in 1 categories – there are many products. So how can i do this??. ANd if i don’t want to give SKu here then whay is coe for array. I want to take dropdown list pls help me

  9. Hello,
    I want my customer to upload multiple photos but want to give just one button. At present if i try to add more photos only the last one gets saved. Please help me in achieving this. I am using magento 1.9. Thanks in advance.

  10. Hi
    thank you for this script and the simple way you explain it. I’m a newer in magento and i wonder where to add this script to got it work. I want to add a insurance as custom options to my products by price range and categories in cart checkout. Can you help me please? thank you

  11. hi, i have a bundle product wich type is packet product, when i try to add it to the cart
    shows this error ¨Please specify product option(s).¨, can you help me i have no idea was going on… i am new in magento. thanks

  12. Pingback: Tips and Strategies for modifying data in the Magento database

  13. Hi,

    I have to add multiple checkboxes with some amount as a custom option for all my products.

    Can you please let me know where I can add above mentioned code?

    Waiting for your reply.

    Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *

Theme: Esquire by Matthew Buchanan.