Magento add new select value/option to products with upgrade script - magento

I want to make a new select attribute option visible in all products.
I have products that each use a select box attribute called "bracket_size". That attribute has three options:
(/admin/catalog_product_attribute/edit/)
Most of the products only have two of these options selected:
(/admin/catalog_product/edit/)
If I select "18mm" in that screen then it shows on the frontend.
I want to create an upgrade script that will set all products to show the "18mm" option.
I had been doing it by selecting all products, fetching them and updating their attribute value:
$options = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'bracket_size')->getSource()->getAllOptions(false);
$option18mmId = $options[0]['value'];
foreach (Mage::getModel('catalog/product')->getCollection() as $product) {
// Get a writable product
$product = Mage::getModel('catalog/product')->load($product->getId());
// All products in these attribute sets should have bracket sizes
$bracketSizeValue = $product->getBracketSize(); // string containing option IDs - something like '645,345'
if (isset($bracketSizeValue)) {
// Get options currently selected for this product
$optionIds = explode(',', $bracketSizeValue);
// Check if the option is already included in this product
if (!in_array($option18mmId, $optionIds)) {
// If not, rebuild the attribute value to add it
array_unshift($optionIds, $option18mmId);
// Add it back to the product
$product->setData('bracket_size', implode(',', $optionIds));
$product->save();
}
}
}
But this doesn't work. It throws an error:
Warning: Invalid argument supplied for foreach() in /.../public/app/code/core/Mage/Eav/Model/Entity/Abstract.php on line 1068
at the $product->save() line.
How can I do this?

If you trying to save products in setup/update script then you need to disable update mode and set current store id first:
$app = Mage::app();
$app->setUpdateMode(false);
$app->setCurrentStore($app::ADMIN_STORE_ID);
Ideally it would be done exactly before saving and then reverted back after saving:
$app->setCurrentStore(null);
$app->setUpdateMode(true);
Then you could also optimise your code my removing loading and saving of each product and doing it on collection items instead.
Loading of collection with desired attribute:
$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToSelect('bracket_code');
Saving of changes on collection items:
$collection->save();

Related

How to programmatically get option id's and values from configurable product in phtml?

I am modifying phtml code were I have access to
$this which happens to be Mage_Checkout_Block_Cart_Item_Renderer_Configurable class
and
$_item = $this->getItem();
$_product = $_item->getProduct();
where product is a configurable product in a cart.
Based on this I need to get a list of option id (swatches) and their values.
For example, to get specific size option id :
$product = $_item->getProduct();
$idStore = $_item->getOrder()->getStoreId();
$sizeSpecificAttributeCode = 'specific_size';
$sizeAttributeOptionId = $product->getResource()
->getAttributeRawValue($product->getId(), $sizeSpecificAttributeCode,$idStore);
from the same logic you can get the swatches or other options

list the values of a product attribute set in magento

how do I find the values of a product's attribute set?
For example, there's a product with an Attribute Set called shirts - T, with attributes of Gender, Shirt Size, and Color. Starting with a $_product object, how do I find the values of the attributes, e.g. Mens, Green, Large?
i am able to getting the attribute set value in the following way:
$product = Mage::getModel('catalog/product')->load($productId);
$prodAttributeSet = Mage::getModel('eav/entity_attribute_set')->load($product->getAttributeSetId())->getAttributeSetName();
I want to get all available attribute set values and codes for specific attribute set(i.e shirt - T)
$_product = Mage::getModel('catalog/product')->load($productId);
Now suppose you want to access value of manufacturer of this product, then consider following code.
$manufacturerValue = $_product->getAttributeText('manufacturer');
As you mention in comment for size and color here i can give you one sample code to use.
If it is a select or multiselect attribute, you still need to map the option ID's to option values. That is the case if you are getting a list of integers instead of human readable labels (e.g. for the color or manufacturer attribute).
// specify the select or multiselect attribute code
$attributeCode = 'color';
// build and filter the product collection
$products = Mage::getResourceModel('catalog/product_collection')
->addAttributeToFilter($attributeCode, array('notnull' => true))
->addAttributeToFilter($attributeCode, array('neq' => ''))
->addAttributeToSelect($attributeCode);
$usedAttributeValues = array_unique($products->getColumnValues($attributeCode));
// ---- this is the different part compared to the previous example ----
// fetch the attribute model
$attributeModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $attributeCode);
// map the option id's to option labels
$usedAttributeValues = $attributeModel->getSource()->getOptionText(
implode(',', $usedAttributeValues)
);
Direct DB query example
Depending on where you want to do this, here is an example of fetching the values without using a product collection. It is slightly more efficient.
Only use the following code in resource models, as thats where DB related code belongs.
This is meant as an educational example to show how to work with Magento's EAV tables.
// specify the attribute code
$attributeCode = 'color';
// get attribute model by attribute code, e.g. 'color'
$attributeModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $attributeCode);
// build select to fetch used attribute value id's
$select = Mage::getSingleton('core/resource')
->getConnection('default_read')->select()
->from($attributeModel->getBackend()->getTable(), 'value')
->where('attribute_id=?', $attributeModel->getId())
->distinct();
// read used values from the db
$usedAttributeValues = Mage::getSingleton('core/resource')
->getConnection('default_read')
->fetchCol($select);
// map used id's to the value labels using the source model
if ($attributeModel->usesSource())
{
$usedAttributeValues = $attributeModel->getSource()->getOptionText(
implode(',', $usedAttributeValues)
);
}
If you want all the attributes of an attribute set you can do the following.
$product = Mage::getModel('catalog/product')->load($productId);
$attributes = $eavConfig->getEntityAttributeCodes( Mage_Catalog_Model_Product::ENTITY, $product );
print_r(attributes);

Creating programatically a category when Flat mode is enabled in Magento

I want to create programatically a category in my data folder of my module. The Flat categories option is enabled.
When a I try to create a category like so:
$category
->setStoreId(0)
->setName('My category')
->setUrlKey('club-campaigns')
->setPath($rootCategory->getPath())
->setIsActive(1)
->setIsAnchor(1)
->setIncludeInMenu(1)
->addData($data)
->setCustomDesignApply(1)
->save();
I get an error which says that catalog_category_flat doesn't exists. Ok, so I know that flat categories info is kept in catalog_category_flat_store_storenumber table. I looked in the database and I have the following tables:
catalog_category_flat_store_1
catalog_category_flat_store_2
catalog_category_flat_store_3
catalog_category_flat_store_4
catalog_category_flat_store_5
catalog_category_flat_store_6
and I want to create a category for store 6. Good, now if I do like this:
$category
->setStoreId(6)
->setName('My category')
->setUrlKey('club-campaigns')
->setPath($rootCategory->getPath())
->setIsActive(1)
->setIsAnchor(1)
->setIncludeInMenu(1)
->addData($data)
->setCustomDesignApply(1)
->save();
the category is created without errors, and it sets the info in catalog_category_flat_store_6 but if I go to admin>Manage Categories and cant't see my category created.
I think that when I create a category I should set te store id of the admin(0) so I can see it in the admin panel but then I get the error above, and if I create with store 6 I don't see it in the admin. I'm really stuck.
How can I properly create my category programatically without problems?
Create category dynamically:
$category = Mage::getModel('catalog/category');
$category->setStoreId(Mage::app()->getStore()->getId());
$cat['name'] = "Custom Category Name here";
$cat['path'] = "1/2/30"; //parent relationship..
$cat['description'] = "categorie's description";
$cat['is_active'] = 1;
$cat['is_anchor'] = 0; //for layered navigation
$cat['page_layout'] = 'two_columns_left';
$cat['url_key'] = "custom-category"; //url to access this category
$cat['image'] = "custom-category.jpg";
$category->addData($cat);
$category->save();
Then reindex catalog_category_flat dynamically:
$process = Mage::getSingleton('index/indexer')->getProcessByCode('catalog_category_flat');
$process->reindexEverything();

Replacing "product name" with "Item + productId" when product is added in Magento via admin panel

I want to auto generate product names in Magento.
When I'm going to add a product, for the product name I will type some string.
When I save the product, I want the product name to be automatically generated such that the product name becomes Item."productId".
Answering assuming that OP wants to incorporate the auto-increment value from the entity table into business data. This is generally not a great idea.
This is an interesting task which can be easily accomplished with Magento's EAV implementation - particularly when working in the catalog module. First, some background.
When an EAV entity is saved, it has a nice, neat array of key => value pairs which represent the attributes and attribute values for that entity:
Mage_Catalog_Model_Product->_data['attribute_code'] = 'attribute value';
During the save process, the EAV resource model takes this array and iterates over it. For each attribute, identified by its code (attribute_code in the above example) and its entity (catalog_product in the case of products), the configuration for the attribute itself is loaded. Of particular importance is the "backend model" for an attribute, as it is invoked to do pre- and post-processing of/relating to the value.
In the current case, there is a piece of information which will not be present when we are saving the attribute, at least, not in a way in which we can use it: the new product id. This can be used to adjust the original value as part of the save process.
It's always nice to have an example from the core, so, refer to the price attribute and its backend model, Mage_Catalog_Model_Product_Attribute_Backend_Price which can be seen in the eav_attribute table:
SELECT `attribute_code`, `backend_model`
FROM `eav_attribute`
LEFT JOIN `eav_entity_type` USING (`entity_type_id`)
WHERE `attribute_code` = 'price';
#+----------------+-----------------------------------------+
#| attribute_code | backend_model |
#+----------------+-----------------------------------------+
#| price | catalog/product_attribute_backend_price |
#+----------------+-----------------------------------------+
#1 row in set (0.00 sec)
When a product is saved, the price attribute's backend_model is instantiated and (in this case) the afterSave() method is called. Incidentally, this method is what updates pricing by conversion rate for website-scoped pricing. This same approach can be used to modify the name attribute.
The following setup script will add the backend model:
<?php
$installer = Mage::getResourceModel('catalog/setup','default_setup');
$installer->startSetup();
$installer->updateAttribute(
'catalog_product',
'name',
'backend_model',
'custom/product_attribute_backend_name'
);
$installer->endSetup();
The corresponding afterSave() method should do the trick:
public function afterSave($object)
{
$value = $object->getData($this->getAttribute()->getAttributeCode());
$origData = $object->getOrigData();
$origValueExist = $origData && array_key_exists($this->getAttribute()->getAttributeCode(), $origData);
//don't do this in case of update
if ($object->getStoreId() != 0 || !$value || $origValueExist) {
return $this;
}
//append autoinc id
$newValue = $value .'.'. $object->getId(); // or whatever
//assume global store, otherwise the stated need is getting weird!
$object->addAttributeUpdate($this->getAttribute()->getAttributeCode(), $newValue, 0);
return $this;
}
If you're doing this from the admin panel product edit screen, you're going to have to remove the "Required" class from the "Name" field so you can save it without the name. This means overriding the Edit form to replace that field specifically. Then you'll have to overload the save-related methods on the product model (or you can do it from the controller), but the child will have to generate the name on save before it goes to the database.
For example:
class Module_Catalog_Model_Product extends Mage_Catalog_Model_Product
{
protected function _beforeSave()
{
parent::_beforeSave();
$productName = 'Item' . $this->getId();
if (!$this->getId() && !$this->getName())
{
$this->setName('Item Unnamed');
} elseif ($this->getId()) && strcasecmp($this->getName(), $productName) <> 0)
{
$this->setName($productName);
}
}
}
The only problem with this is that it requires two saves. If you want to have it work on the fly, you'll have to do a second save using the _afterSave() method. Or, once again, you can do it from the controller.
I would use a Magento Event to do this:
Since Models in Magento have an event prefixes (just take a look at Mage_Catalog_Model_Product and look for $_eventPrefix, for our current model the eventPrefix is set to catalog_product.
If you now take a look at Mage_Core_Model_Abstract and search for _eventPrefix. You see that eventPrefix are found in _beforeLoad, _afterLoad, _beforeSave, _afterSave and a few others. In these methods you can see an event is dispatched using something as below:
Mage::dispatchEvent($this->_eventPrefix.'_save_before', $this->_getEventData());
This means you have an event available called catalog_product_save_before. With this event you can hook into Magento at that time and do your thing, change the field in this case, and Magento handles the rest.
Take a look at http://www.magentocommerce.com/wiki/5_-_modules_and_development/0_-_module_development_in_magento/customizing_magento_using_event-observer_method for more information how to use these events and turn them into a module. If you don't know how to build modules for Magento and want to learn, there are some awesome on-demand video's for free: http://www.magentocommerce.com/training/on-demand
First I want to thanks to all users which write in the topic. Thanks a lot of guys!
I did it, but I make it easier. (because I have very basic knowledge in Magento and it would toke more time)
So... With my colegues decided to make it with php/jquery/ajax.
First we create one single php file, which return the last id:
<?php
header('Access-Control-Allow-Origin: *');
require_once 'app/Mage.php';
umask(o);
Mage::app('default');
Mage::getSingleton('core/session', array('name'=>'frontend'));
$model = Mage::getModel('catalog/product'); //getting product model
$collection = $model->getCollection(); //products collection
foreach ($collection as $product) //loop for getting products
{
$id=$product->getId();
}
if($id)echo $id+1; //id of product
else{
echo 0;
}
?>
After step one I set the value of input (i.e. I auto generate the name):
if($j('#name').val()=='' && window.location.href.indexOf("admin/catalog_product/new/") > -1) {
$j.post('http://www.website.com/file.php', function(data) {
$j('#name').val('Item №'+data);
});
}
Thanks again for help.
Best Regards,
Jordan!

Looping through Magento attribute values skips the first item

This is an ajax file running the following code:
$model = Mage::getModel('catalog/product'); //getting product model
foreach ($violins as $k => $v)
{
$_product = $model->load($v); //getting product object for particular product id
$violinmodel = $_product->getAttributeText('Violinmodel'); //grabbing the violinmodel attribute value
echo $violinmodel;
}
$violin contains an array with three product ID's. My output is echoing the attribute value for the second and third ID's fine, but is NOT echoing the first ID!
I don't get this at all! Why would it completely skip the first ID in the loop and not echo anything, while echoing the following ID's without a problem?
The attributes are set up properly and no matter how I rearrange the ID's in the $violins array, the first attribute value is always skipped. What am I missing?
Initializing $model outside of your loop is unsafe. You might think that you are being more efficient with memory and/or function calls but you're asking for trouble. The Mage_Catalog_Model_Product object is being loaded at that time, and calling ->load() isn't giving you a new object, it's just setting the data of your existing one. Except, you'll get strange behavior when not all of the data gets overwritten (for example if ProductA has a Violinmodel attribute and ProductB doesn't... it will look like ProductA.Violinmodel == ProductB.Violinmodel). For that reason, you should always put your model inside your loop.
foreach ($violins as $k => $v) {
$_product = Mage::getModel('catalog/product')->load($v); //getting product
if ($_product->getId() == $v) { // sanity check
$violinmodel = $_product->getAttributeText('Violinmodel'); //grabbing the violinmodel attribute value
echo $violinmodel;
}
}
Or, as benmarks suggested, load this data via a collection:
$_products = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('Violinmodel')
->addIdFilter($violins);
foreach ($_products as $_product) {
echo $_product->getAttributeText('Violinmodel'));
}
When you iterate over a product collection in Magento, the items it contains are actually product object instances. What you are doing here (hitting the database multiple times, loading all attribute) is unnecessary given what you are trying to accomplish (getting one attribute). Try adding the attribute to the collection to begin with and iterate over it:
$coll = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('Violinmodel');
//be certain that the attribute code is capitalized...
foreach ($coll as $product) {
//var_dump($product->debug()); //for example
var_dump($product->getAttributeText('Violinmodel));
}

Resources