7

I already added custom tab and custom attribute like this.

Product attribute custom template

I also created a custom column in database for attributes, called custom_values.

Now I want to add custom template for this attribute (in picture, attribute is Active)

In my custom template, it contains a form (hard code maybe). When save product, I want to serialize form data then assign to custom_values of Active attribute

Questions are:

  1. How to add custom template for specific product attribute in adminhtml

  2. How to observe product save event to serialize form data then assign value before it actually saves

Tran Dinh Khanh
  • 724
  • 1
  • 9
  • 19

1 Answers1

15

First of all...nice question. Got me really interested.
Here is an example on how you can create an attribute that has a custom renderer (template) and the value is saved serialized.
For this example I created an attribute that will have 2 text inputs, but you can basically put anything in there.

I recommend creating a custom module to handle your attribute. Let's call this extension Easylife_Attr.
You will need the following files.
app/etc/module/Easylife_Attr.xml - the declaration file.

<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Attr>
            <codePool>local</codePool>
            <active>true</active>
            <depends>
                <Mage_Catalog /><!-- should depend on mage_catalog-->
                <Mage_Adminhtml /><!-- should depend on mage_adminhtml-->
            </depends>
        </Easylife_Attr>
    </modules>
</config>

app/code/local/Easylife/Attr/etc/config.xml - the configuration file

<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Attr>
            <version>0.0.1</version>
        </Easylife_Attr>
    </modules>
    <global>
        <resources>
            <easylife_attr_setup>
                <setup>
                    <module>Easylife_Attr</module>
                    <class>Mage_Catalog_Model_Resource_Setup</class><!-- use the catalog setup so you can add your attribute -->
                </setup>
            </easylife_attr_setup>
        </resources>
        <models>
            <easylife_attr>
                <class>Easylife_Attr_Model</class>
            </easylife_attr>
        </models>
        <blocks>
            <easylife_attr>
                <class>Easylife_Attr_Block</class>
            </easylife_attr>
        </blocks>
    </global>
    <adminhtml>
        <events>
            <adminhtml_catalog_product_edit_prepare_form><!-- event needed to add a template to a certain attribute -->
                <observers>
                    <easylife>
                        <class>Easylife_Attr_Model_Observer</class>
                        <method>convertCustomValues</method>
                    </easylife>
                </observers>
            </adminhtml_catalog_product_edit_prepare_form>
        </events>
    </adminhtml>
</config>

app/code/local/Easylife/Attr/sql/easylife_attr_setup/install-0.0.1.php - the install script. It will add your attribute

<?php
$this->addAttribute('catalog_product', 'custom_values', array(
    'group'         => 'Custom values', //the tab name where the attribute will be placed
    'input'         => 'textarea', //this is not really important
    'type'          => 'text', //attribute type should be text to support long values
    'label'         => 'Custom values', //the attribute label
    'backend'       => 'easylife_attr/custom',  //a custom backend model that will handle serialization and deserialization
    'visible'       => true,
    'required'      => false,
    'visible_on_front' => true,
    'global'        => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL, //scope can be anything but if it's not global you will need some changes to support store view values
));

Now the fun part.

app/code/local/Easylife/Attr/Model/Observer.php - the observe that will change the attribute template

<?php
class Easylife_Attr_Model_Observer {
    public function convertCustomValues($observer) {
        $form = $observer->getEvent()->getForm();
        $customValues = $form->getElement('custom_values');
        if ($customValues) {
            $customValues->setRenderer(
                Mage::app()->getLayout()->createBlock('easylife_attr/adminhtml_product_custom')
            ); //set a custom renderer to your attribute
        }
    }
}

app/code/local/Easylife/Attr/Block/Adminhtml/Product/Custom.php - the custom renderer block

<?php
class Easylife_Attr_Block_Adminhtml_Product_Custom
    extends Mage_Adminhtml_Block_Widget
    implements Varien_Data_Form_Element_Renderer_Interface {
    public function __construct()
    {
        $this->setTemplate('easylife_attr/product/custom.phtml'); //set a template
    }
    public function render(Varien_Data_Form_Element_Abstract $element) {
        $this->setElement($element);
        return $this->toHtml();
    }
}

app/design/adminhtml/default/default/template/easylife_attr/product/custom.phtml - the template for the attribute.

<?php
$_htmlId = $this->getElement()->getHtmlId();
$_htmlClass = $this->getElement()->getClass();
$_htmlName = $this->getElement()->getName();
$_readonly = $this->getElement()->getReadonly();
$value = $this->getElement()->getValue();
//get the values for the 2 text elements
$someField = (isset($value['some_field']) ? $value['some_field'] : '');
$otherField = (isset($value['other_field']) ? $value['other_field'] : '');

?>
<tr> <!-- should be wrapped in a tr element to fit in the admin template -->
    <td class="label"><?php echo $this->getElement()->getLabel(); ?></td>
    <td colspan="10" class="grid">
        <table cellspacing="0" class="data border">
            <col width="120" />
            <col />
            <thead>
            <tr class="headings">
                <th><?php echo $this->__('Some field')?></th>
                <th><?php echo $this->__('Other Field')?></th>
            </tr>
            </thead>
            <tbody id="<?php echo $_htmlId; ?>_container"></tbody>
            <tfoot>
            <tr>
                <td><input type="text" name="<?php echo $_htmlName; ?>[some_field]" value="<?php echo $someField?>"<?php echo ($_readonly) ? ' readonly="readonly"' : ''?>> </td>
                <td><input type="text" name="<?php echo $_htmlName; ?>[other_field]" value="<?php echo $otherField?>"<?php echo ($_readonly) ? ' readonly="readonly"' : ''?>></td>
            </tr>
            </tfoot>
        </table>
    </td>
</tr>

app/code/local/Easylife/Attr/Model/Custom.php - the model that handles the serialization and deserialization.

<?php
class Easylife_Attr_Model_Custom extends Mage_Eav_Model_Entity_Attribute_Backend_Abstract{
    public function beforeSave($object) 
    {
        //before sabing the product check if the attribute `custom_values` is array.
        //if it is, serialize it for saving in the db
        $attributeCode = $this->getAttribute()->getAttributeCode();
        $data = $object->getData($attributeCode);
        if (is_array($data)) {
            $data = array_filter($data);
            $object->setData($attributeCode, serialize($data));
        }
        return parent::beforeSave($object);
    }
    public function afterLoad($object) {
        //after loading the product, check if the value for custom_values is not an array. If it's not try to unserialize the value.
        $attributeCode = $this->getAttribute()->getAttributeCode();
        $data = $object->getData($attributeCode);
        if (!is_array($data)) {
            $object->setData($attributeCode, @unserialize($data));
        }
        return parent::afterLoad($object);
    }
}

That's it. Here is how the attribute would look in the backend:

Custom template attribute

Marius
  • 197,939
  • 53
  • 422
  • 830
  • You are the man! Always make people happy with your answer. I try this right now and then mark your answer accepted. Thank you, thank you. – Tran Dinh Khanh Oct 09 '14 at 07:23
  • but how to add images, editor for textarea, calender for date field. – Pradeep Singh Jul 14 '15 at 10:51
  • @PradeepSingh. As explained in the answer, what you see in the custom tab is rendered by a phtml file app/design/adminhtml/default/default/template/easylife_attr/product/custom.phtml. You can basically add anything you want in that phtml file. Any types of fields or javascripts you need. – Marius Jul 14 '15 at 11:25
  • @Marius. I want to add multiple textboxes and editor means one block have a one text box and one editor and two date field and one select box and upload multiple images button. After this Add More button is there.then Click on Add button you can add another block using ajax means you can add block1, block2..... How to work? – Pradeep Singh Jul 16 '15 at 01:12
  • @Marius any way to work as mention above – Pradeep Singh Jul 16 '15 at 06:23
  • @PradeepSingh. Yes you can make it work. Just check how the tier prices work in Magento. I assume you need a similar thing but with other fields. The principle is the same. I used the tier prices behavior as an example to write the answer above. – Marius Jul 16 '15 at 06:31
  • @Marius thanks for reply.Is it right to save all in one field in database – Pradeep Singh Jul 16 '15 at 06:53
  • @Marius I have added jquery to load same block again and again. its working.I want to save in database – Pradeep Singh Jul 16 '15 at 06:58
  • @Marius any link where I see tier price behavior – Pradeep Singh Jul 16 '15 at 07:03
  • https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Price/Tier.php and this https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/design/adminhtml/default/default/template/catalog/product/edit/price/tier.phtml – Marius Jul 16 '15 at 07:16
  • I cannot overstate how helpful this was! Major thanks, @Marius! – jschrab Mar 04 '16 at 22:43
  • @Marius ! In Magento 2 - How to customize template for attribute of product ? – MrTo-Kane Mar 18 '16 at 03:09
  • This is important! The code above works for packing multi-value content into a single string attribute - BUT it won't cover the case of needing to delete row values on the beforeSave() end of things. Magento grid-controls set a 'delete' value for each row to delete to '1' if the item is to be deleted, which you need to loop through your $data array to look for and remove from the array before serialization. I'm oversimplifying here - look closely at the hidden delete fields managed in the Tier Price grid to see what I'm talking about. – jschrab Jun 28 '16 at 17:43
  • @jschrab. I agree. But the question asked for a simple template to be rendered. There is no geid involved here. – Marius Jun 28 '16 at 18:01
  • 1
    @Marius is there any trick available same like this in Magento2 ? Because magento2 using UI component. – Keyur Shah Jun 30 '16 at 13:18
  • @KeyurShah. I don't know. I haven't checked that yet. – Marius Jun 30 '16 at 13:22
  • @Marius Any clue for that ? – Keyur Shah Jun 30 '16 at 13:23
  • @KeyurShah I just said I don't know. – Marius Jun 30 '16 at 13:28
  • @KeyurShah, perhaps this post will help you: http://magento.stackexchange.com/questions/122957/add-new-tab-in-product-edit-page-and-call-custom-phtml-in-magento-2-1 – Luuk Skeur Aug 10 '16 at 13:09