Extend Magento's EAV Attribute Model - php

I would like to add a new attribute to Magento's EAV attribute model. Is this possible?
I know that Magento lets you extend models using static fields (which are on the entity table), but I would like to add a new field to the EAV attribute table itself (for catalog product attributes). The new attribute will be a new setting, similar to "Visible in Category List".

To add a new setting for product attributes, you can create an extension that (1) adds the new column to the catalog/eav_attribute table, and (2) puts in a field for the new setting in the attribute edit page using an observer.
(1) Create your new DB field (is_visible_in_category_list)
Make an SQL script for your extension, and add the new column. I would recommend using the catalog/eav_attribute table, but you could probably use eav_attribute as well:
$installer = $this;
$installer->startSetup();
$table = $installer->getTable('catalog/eav_attribute');
$installer->getConnection()->addColumn(
$table,
'is_visible_in_category_list',
"TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'"
);
$installer->getConnection()->addKey(
$table,
'IDX_VISIBLE_IN_CATEGORY_LIST',
'is_visible_in_category_list'
);
$installer->endSetup();
Here, I also added an index for faster querying.
(2) Add the field to the edit product attribute page
An event is fired when preparing the attribute edit form, so let's observe it:
<events>
<adminhtml_catalog_product_attribute_edit_prepare_form>
<observers>
<is_visible_in_category_list_observer>
<class>mymodule/observer</class>
<method>addVisibleInCategoryListAttributeField</method>
</is_visible_in_category_list_observer>
</observers>
</adminhtml_catalog_product_attribute_edit_prepare_form>
</events>
Then, add the new field in the observer:
public function addVisibleInCategoryListAttributeField($observer)
{
$fieldset = $observer->getForm()->getElement('base_fieldset');
$attribute = $observer->getAttribute();
$fieldset->addField('is_visible_in_category_list', 'select', array(
'name' => 'is_visible_in_category_list',
'label' => Mage::helper('mymodule')->__('Visible in Category List'),
'title' => Mage::helper('mymodule')->__('Visible in Category List'),
'values' => Mage::getModel('adminhtml/system_config_source_yesno')->toOptionArray(),
));
}
That's all. Saving the setting from the edit page is automatically handled because the field name in the form matches the DB field name.

Related

How to add custom product fields to prestashop using a module and hooks

I need to add some fields to prestashop product (HSN code and one more). I am very new to prestashop and there is no guide to do the same with latest build 1.7.
I have followed answers made on stackoverflow and I am able to show the form fields but unable to save and validate the value.
Here is the code snippet I have used (I preferred this because it uses the hooks).
use PrestaShopBundle\Form\Admin\Type\TranslateType;
use PrestaShopBundle\Form\Admin\Type\FormattedTextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
public function hookDisplayAdminProductsExtra($params)
{
$productAdapter = $this->get('prestashop.adapter.data_provider.product');
$product = $productAdapter->getProduct($params['id_product']);
$formData = [
'ebay_reference' => $product->ebay_reference,
];
$formFactory = $this->get('form.factory');
$form = $formFactory->createBuilder(FormType::class, $formData)
->add('ebay_reference', TranslateType::class, array(
'required' => false,
'label' => 'Ebay reference',
'locales' => Language::getLanguages(),
'hideTabs' => true,
'required' => false
))
->getForm()
;
return $this->get('twig')->render(_PS_MODULE_DIR_.'MyModule/views/display-admin-products-extra.html.twig', [
'form' => $form->createView()
]) ;
}
public function hookActionAdminProductsControllerSaveBefore($params)
{
$productAdapter = $this->get('prestashop.adapter.data_provider.product');
$product = $productAdapter->getProduct($_REQUEST['form']['id_product']);
foreach(Language::getLanguages() as $language){
$product->ebay_reference[ $language['id_lang'] ] =
$_REQUEST['form']['ebay_reference'][$language['id_lang']];
}
$product->save();
}
I am stucked at data saving part. Need some guidance to it in recommended way. Also need the suggestion to read the code of any module bundled with prestashop to help in this.
Add field in product Prestashop 1.7
This part of the code just describes how to create a form with necessary fields but it doesn't handle product class extending. So if you would have this attribute(ebay_reference) with all relations in your product class everything would work. So I suppose that you need to implement steps for /classes/Product.php and for src/PrestaShopBundle/Model/Product/AdminModelAdapter.php from the original answer of here Add field in product Prestashop 1.7 and add necessary field to the DB.
Also, if you don't want to modify or override default product class, you can create own table(s) to keep your data there like with id_product key, but it could be more difficult to propagate that data to all product instances in the store.

Magento hidden custom product attributes

I am currently making a module so that users see on the checkout page something like: "People who purchased product X also purchased Y, Z and T".
I made a cronjob to calculate which are the related products for each product, and I added an attribute to products in the install script.
I decided (for simplicity) - to store the most related 5 products, so I want to store something like: 123-3-5543-1290-9911. But I don't want the administrator to see this anywhere, and I tried the following:
$setup->addAttribute('catalog_product', $attrCode, array(
// more stuff
'type' => 'hidden',
'input' => 'text',
'visible' => 0,
// more stuff
));
I looked here: http://blog.chapagain.com.np/magento-adding-attribute-from-mysql-setup-file/ and I found some interesting stuff, but not how to completely hide this field.
The alternative would be to create my own table, but this seems to be a slightly more elegant solution.
What do you think? Is it better to create my own table, or to add an attribute and hide it?
Thank you
Setting the 'is_visible' property to 0 for catalog attributes will hide them from the admin forms in the backend, as can be seen from this code (Mage_Adminhtml_Block_Widget_Form::_setFieldset()):
protected function _setFieldset($attributes, $fieldset, $exclude=array())
{
$this->_addElementTypes($fieldset);
foreach ($attributes as $attribute) {
/* #var $attribute Mage_Eav_Model_Entity_Attribute */
if (!$attribute || ($attribute->hasIsVisible() && !$attribute->getIsVisible())) {
continue;
}
So execute
$setup->updateAttribute('catalog_product', $attrCode, 'is_visible', '0');
I solve it in this way.
Go to Catalog >> Attributes >> Manage Attributes >> Add new attribute
Then, save. Then, Go to database and Run this query:
SELECT * FROM eav_attribute WHERE attribute_code ='attribute_code';
Change the column frontend_input value to hidden, then save.
Hope it helps.

Add product selection widget to custom attribute in Magento

Does anyone know how can I add a custom product attribute with a widget renderer?
You can see this in Promo rules if you select SKU you'll got an Ajax popup with product selection.
so how would I go about it?
in :
$installer->addAttribute(Mage_Catalog_Model_Product::ENTITY...
In other words, how can I use a widget to select custom attribute values?
EDIT:
The scenario is as follows:
I would like to create a product attribute that will, upon a button click, open a product selection widget.
After the selection, the selected SKU's will go in in a comma delimited format.
This behavior can be seen in the catalog and shopping cart price rules.
If you filter the rule by SKU (SKU attribute must be enabled to "apply to rules"), you'll get a field and a button that will open the product selection widget.
Here is some thoughts that should get you going on the right track:
First, in a setup script, create your entity:
$installer->addAttribute('catalog_product', 'frontend_display', array(
'label' => 'Display Test',
'type' => 'varchar',
'frontend_model' => 'Test_Module/Entity_Attribute_Frontend_CsvExport',
'input' => 'select',
'required' => 0,
'user_defined' => false,
'group' => 'General'
));
Make sure to set the frontend_model to the model that you are going to use. The frontend model affects the display of the attribute (both in the frontend and the adminhtml sections).
Next, create yourself the class, and override one or both of the following functions:
public function getInputType()
{
return parent::getInputType();
}
public function getInputRendererClass()
{
return "Test_Module_Block_Adminhtml_Entity_Renderer_CsvExport";
}
The first (getInputType()) is used to change the input type to a baked in input type (see Varien_Data_Form_Element_* for the options). However, to set your own renderer class, use the latter function - getInputRendererClass(). That is what I am going to demonstrate below:
public function getElementHtml()
{
return Mage::app()->getLayout()->createBlock('Test_Module/Adminhtml_ExportCsv', 'export')->toHtml();
}
Here, to clean things up, I am instantiating another block, as the element itself doesn't have the extra functions to display buttons and the like.
Then finally, create this file:
class Test_Module_Block_Adminhtml_ExportCsv extends Mage_Adminhtml_Block_Widget
{
protected function _prepareLayout()
{
$button = $this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => $this->__('Generate CSV'),
'onclick' => '',
'class' => 'ajax',
));
$this->setChild('generate', $button);
}
protected function _toHtml()
{
return $this->getChildHtml();
}
}
This doesn't cover the AJAX part, but will get you very close to getting the rest to work.

Extending Magento Shopping Cart

I need to extend the Magento shopping cart to include an extra step for a store locator. I understand that I need to overwrite the core OnePage controller (Mage_Checkout_OnepageController) and blocks (Mage_Checkout_Block_Onepage) but what needs to be done with regards to keeping track of the extra information (e.g. user's selected options from my custom step).
There are a number of steps required here to get the whole solution.
Firstly, create a new module. Use the ModuleCreator if you wish.
Then, write a setup script in your module to add the new fields to Magento's attribute structure, e.g. :
$setup = new Mage_Sales_Model_Mysql4_Setup('core_setup');
$setup->startSetup();
$setup->addAttribute('quote', 'my_attribute', array('type' => 'varchar', 'visible' => false, 'required' => false));
$setup->addAttribute('order', 'my_attribute', array('type' => 'varchar', 'visible' => false, 'required' => false));
$setup->addAttribute('invoice', 'my_attribute', array('type' => 'varchar', 'visible' => false, 'required' => false));
$setup->addAttribute('creditmemo', 'my_attribute', array('type' => 'varchar', 'visible' => false, 'required' => false));
Note the use of the Mage_Sales_Model_Mysql4_Setup to add the fields to the relevant sales_flat_quote and sales_flat_order tables.
Now, insert the following values in your module's config.xml file:
<global>
<fieldsets>
<sales_convert_quote>
<my_attribute>
<to_order>*</to_order>
</my_attribute>
</sales_convert_quote>
<sales_convert_order>
<my_attribute>
<to_cm>*</to_cm>
<to_invoice>*</to_invoice>
</my_attribute>
</sales_convert_order>
</fieldsets>
That will instruct Magento to copy the values of your custom field from quote to order to invoice and credit_memo, etc.
Then in your custom block/controller code, you will be able to use Magento's magic getters and setters to persist the values.
$oQuote = Mage::getSingleton('checkout/session')->getQuote();
$oQuote->setMyAttribute('some_value');
$oQuote->save();
You should see the new column and value saved in sales_flat_quote. Then once the customer completes checkout, the same value should be saved in sales_flat_order.
Note that the above code can be extended to work for quote_item and order_item by just changing quote to quote_item etc, however, if you wish to save attribute values that have been set on your products, then some extra work is required.
Insert a new block of XML into your config.xml (again inside the global node):
<sales>
<quote>
<item>
<product_attributes>
<my_attribute />
</product_attributes>
</item>
</quote>
</sales>
Where my_attribute is the attribute code on the product model. That will make the my_attribute available on the linked product, so you can access it via
$oQuoteItem->getProduct()->getMyAttribute()
without needing to perform a full Mage::getModel('catalog/product')->load($oQuoteItem->getProductId()). This is much more efficient.
Then, you will need an observer to copy the values from the product object to the quote_item object. So, declare your observer in the config.xml:
<events>
<sales_quote_item_set_product>
<observers>
<quoteitem_set_custom_data>
<type>singleton</type>
<class>mymodule/observer</class>
<method>setCustomDataOnQuoteItem</method>
</quoteitem_set_custom_data>
</observers>
</sales_quote_item_set_product>
<events>
and write code in your observer class like this:
public function setCustomDataOnQuoteItem($oObserver){
$oProduct = $oObserver->getProduct();
$oQuoteItem = $oObserver->getQuoteItem();
foreach(array('my_attribute') as $vAttributeCode){
$oQuoteItem->setData($vAttributeCode,$oProduct->getData($vAttributeCode));
}
}
Here is a complete working module.. its (almost) the same as the above code of Johnatan.
You will find it here:
https://bitbucket.org/vovsky/adding-custom-product-attribute-to-quote-and-order-items-in/
And full explanation of every step here: http://www.atwix.com/magento/custom-product-attribute-quote-order-item/

Programmatically updating/editing an attribute's option in Magento

I have an attribute called "vendor_id". I have N products that have the "vendor_id" as an attribute of the product. The options for the "vendor_id" attribute are being generated programmatically when an admin adds a new "vendor" entity. The code is like this:
public function saveAction()
{
$data = $this->getRequest()->getPost();
$designerName = $data['title'];
$product = Mage::getModel('catalog/product');
$attributes = Mage::getResourceModel('eav/entity_attribute_collection')
->setEntityTypeFilter($product->getResource()->getTypeId())
->addFieldToFilter('attribute_code', 'vendor_id')
->load(false);
$attribute = $attributes->getFirstItem()->setEntity($product->getResource());
$myresults = array ('value'=> array('optionone'=>array($designerName)));
$attribute->setData('option',$myresults);
$attribute->save();
And this works for now. It will create an option for the "vendor_id" attribute, and when the user adds a new product (or edit's an existing product) the drop down for "vendor_id" will be populated by these "vendor" entities we've created on the saveAction() method.
Now, in the case an admin wants to edit an existing attribute option, I don't want to create a new option, I want to edit an existing one. It's important that the option ID stays the same when we change the name/label.
I've hooked in the newAction to set a static var so in the saveAction() we can check and see if we are editing or creating a new option:
if (null == MyController::$_editScope)
{
error_log('Need to update option attribute');
$attribute->addData($myresults);
}
The problem is that the addData() method does just that, it adds the data, but doesn't update the existing on. The attribute is:
$attribute = $attributes->getFirstItem()->setEntity($product->getResource());
Which is an instance of: http://docs.magentocommerce.com/Mage_Eav/Mage_Eav_Model_Entity_Attribute.html
Which has 3x parent classes and I've looked through them all for a method that will allow me to edit* or update* an existing option's name ...
You can find this useful. See complete details here.
//Get the eav attribute model
$attr_model = Mage::getModel('catalog/resource_eav_attribute');
//Load the particular attribute by id
//Here 73 is the id of 'manufacturer' attribute
$attr_model->load(73);
//Create an array to store the attribute data
$data = array();
//Create options array
$values = array(
//15 is the option_id of the option in 'eav_attribute_option_value' table
15 => array(
0 => 'Apple' //0 is current store id, Apple is the new label for the option
),
16 => array(
0 => 'HTC'
),
17 => array(
0 => 'Microsoft'
),
);
//Add the option values to the data
$data['option']['value'] = $values;
//Add data to our attribute model
$attr_model->addData($data);
//Save the updated model
try {
$attr_model->save();
$session = Mage::getSingleton('adminhtml/session');
$session->addSuccess(
Mage::helper('catalog')->__('The product attribute has been saved.'));
/**
* Clear translation cache because attribute labels are stored in translation
*/
Mage::app()->cleanCache(array(Mage_Core_Model_Translate::CACHE_TAG));
$session->setAttributeData(false);
return;
} catch (Exception $e) {
$session->addError($e->getMessage());
$session->setAttributeData($data);
return;
}

Categories