I made a simple import script and I'm trying to programatically save 3 custom attributes (att1, att2, att3) together with all other info (name, description, price, category..).
So basically I have:
public function insert_product($data) {
$product = Mage::getModel('catalog/product');
try {
$sku = $data['code'];
if ($this->prodottiImportati[$sku]) {
$sku = $data['code'] . '-1';
}
$this->prodottiImportati[$sku] = true;
$product->setSku($sku);
$product->setName($data['name']);
$product->setDescription($data['desc']);
$product->setShortDescription($data['short_desc']);
$product->setManufacturer('');
$product->setPrice($data['price']);
$product->setTypeId('simple');
$product->setAttributeSetId($this->attributeSet);
$categorie = $this->get_categories($data);
$product->setCategoryIds($categorie);
$product->setWeight($data['peso']);
$product->setTaxClassId(2); // taxable goods
$product->setVisibility(4); // catalog, search
$product->setStatus(1); // enabled
$product->setWebsiteIds($data['store_id']);
$stockData = $product->getStockData();
$stockData['qty'] = $data['qty'];
if ($data['quantita'] > 0) {
$stockData['is_in_stock'] = 1;
} else {
$stockData['is_in_stock'] = 0;
}
$stockData['manage_stock'] = 1;
$stockData['use_config_manage_stock'] = 0;
$product->setStockData($stockData);
$product->setIsMassupdate(true)->setExcludeUrlRewrite(true);
$product->save();
$productID = $product->getId();
} catch(Exception $e) {
echo ($e->getMessage());
}
return $productID;
}
First thing I tryed was adding a
$productID = $this->insert_product($data);
Mage::getSingleton('catalog/product_action')->updateAttributes(
array($productID), array(
'att1' => $data['att1'],
), $data['store_id']);
So basically updating things after the insert function was called, using the ID got after the insert. store_id is the ID of the store in that given language. Didn't save anything.
Second attempt, I follwed this: Magento add custom options with programmatically importing products
I tryed that within the insert_product function and also outside after $productID = $this->insert_product($data); Neither worked.
Last I tryed a magical $product->setAtt1('value'); witin the insert_product function, not sure how Magento would understand how to set att1 that way, but...you know, I read it somewhere and I gave it a try ;)
att1, att2 and att3 are spelled lowercase, althoug they have an uppercase label (think that dosen't matter here), they are part of an attribute group (I'm passing it with $product->setAttributeSetId($this->setAttributi)) and they are all multiple selection attributes, so I could in teory pass multiple values to them.
I'm sure I'm missing something on the way. Can anyone help?
After 10 more minutes since I wrote here, I was able to find the way. I took me forever to solve it.
The clue of this is that you have to add attributes ID, not values. That happens at least for me with multiple selection attributes, not sure if it's true all the time.
Here is how I did:
In the function insert_product I added:
$optionId = $this->get_option_id_by_code('att1', 'Value of the attribute you need to add');
$product->setAtt1($optionId);
So if yor attribute is named, let's say "brand" it will be:
$optionId = $this->get_option_id_by_code('brand', 'Nike');
$product->setBrand($optionId);
If your attribute can have multiple values, you need to change the code above to:
$optionId = array();
foreach ($myAttributesArray as $someValues) {
$optionId[] = $this->get_option_id_by_code('att1', $someValues);
}
$product->setAtt1($optionId);
The foreach is just an example, you need to loop through your mutiple values and get the respective ID and add them all toghether with setAtt1 passing them as an array. I'm working on an improved version where the get_option_id_by_code function does all at once in a more efficient way. This is kust a "basic" version that works, feel free to make it fancy and smart.
Then I created an other function called get_option_id_by_code like this:
protected function get_option_id_by_code($attrCode, $optionLabel) {
$attrModel = Mage::getModel('eav/entity_attribute');
$attrID = $attrModel->getIdByCode('catalog_product', $attrCode);
$attribute = $attrModel->load($attrID);
$options = Mage::getModel('eav/entity_attribute_source_table')
->setAttribute($attribute)
->getAllOptions(false);
foreach ($options as $option) {
if ($option['label'] == $optionLabel) {
return $option['value'];
}
}
return false;
}
To be honest I found this with a collage of other sources / authors, so I need to be thankful to a bunch of smarter programmers here and there, since it took a while for me to strouggle with this simple task I wrote the solution here hoping to help you guys. Thanks!
Related
I am using the below code in functions.php to add custom query variables to my WordPress project.
<?php
function add_custom_query_var( $vars ){
$vars[] = "brand";
return $vars;
}
add_filter( 'query_vars', 'add_custom_query_var' );
?>
And then on the content page I am using:
<?php echo $currentbrand = get_query_var('brand'); ?>
So now I can pass a URL like:
http://myexamplesite.com/store/?brand=nike
and get nike in result.
How can I pass multiple values for a brand. So I would like to have 3 values for brand say, nike, adidas and woodland.
I have tried the following and none of them works:
http://myexamplesite.com/store/?brand=nike&brand=adidas&brand=woodland
It just returns the last brand, in this case woodland.
http://myexamplesite.com/store/?brand=nike+adidas+woodland
This returns all three with spaces. I kind of think this is not the correct solution. So may be there is a correct way to pass multiple values for a query variable and retrieve them in an array may be so that a loop can be run.
Usually you would pass the parameter like ?brand[]=unresponsibleCompany1&brand[]=otherBrandThatDoesNotCareMuch&brand[]=bunchOfCriminals .
In your WordPress PHP code, then get_query_var( 'brand' ) will return you an Array.
Everything else sounds like a relatively cheap but costy workaround.
You will need to pass query parameters in this way;
brand1=nike&brand2=adidas&brand3=woodland
So, different key for each brand
On the page, recieving values
$params = $_GET;
$brands = array();
foreach($params as $k=>$v) {
if(substr($k,0,5)=="brand") {
$brands[] = $v;
}
}
Alternatively
Using your 2nd method
http://myexamplesite.com/store/?brand=nike+adidas+woodland
$brands = explode(" ", $_GET['brand']);
Alternative method looks easier 🙂
I want to apply search filters in my project. I have options tables where options are being saved with the option values with parent id of option id. For example brand is saving as option with parent id set to 0 and all brands have brand id as their parent id set and while saving product I am saving product options in product_options table. Now i want to apply filters in product listing page. I am using following code for filtration:
$conditions = array();
$product_options = $this->ProductOption->find('list',array('fields'=>array('product_id'),'conditions'=>array('ProductOption.option_value_id'=>$data['data']['options'])));
$conditions = array_merge($conditions,array('Product.id'=>array_unique($product_options)));
$prod_info = $this->paginate('Product',$conditions);
$this->set(compact('prod_info'));
When I search any product with their brand name it works fine but if I try to search with the price (also an option) then it gives other brand products also which have price equal to filter price option. Please check following link to understand problem correctly.
http://primemart.in/Food-Processors-Ii4zRGAKYAo=
Please anyone help me to come out my problem.
Thanks.
Please have a look on my code which I used to pass conditions in and to get results
$product_options = $this->ProductOption->find('list',array(
'fields'=>array('product_id'),
'conditions'=>array('ProductOption.option_value_id'=>$data['data']['options'])
));
//$this->Option->unBindModel(array('belongsTo'=>'Product'));
$product_options = $this->Option->find('all', array(
'conditions'=>array('Option.id'=>$data['data']['options'])
));
//pr($product_options);
$opt_arr = array();
foreach ($product_options as $op) {
$opt_arr[$op['Option']['parent_id']][] = $op['Option']['id'];
}
$conditions_arr = array();
foreach($opt_arr as $opt) {
$key_arr = array();
foreach($opt as $op) {
$key_arr['OR']['ProductOption.option_value_id'][] = $op;
}
$conditions_arr['AND'][] = $key_arr;
}
$pr_options = $this->ProductOption->find('list', array(
'conditions'=>$conditions_arr,
'fields'=>array('product_id')
));
$conditions = array_merge($conditions, array('Product.id'=>array_unique($pr_options)));
I would try code bellow. I assume that $conditions constist of the other conditions you mention in your question.
$conditions = ... // other conditions you mentioned
$conditions = array('AND'=>array($conditions, array('Product.id'=>array_unique($product_options))));
$prod_info = $this->paginate('Product',$conditions);
I am creating custom emails to be sent to customers wanting to share their cart with others. So far, I have each product's quantity, SKU, price, path (node/XYZ), and title. The last item I need in the email is the product's image path.
I found all the other information with the following:
$order = commerce_cart_order_load($user->uid);
foreach($wrapper->commerce_line_items as $d => $line_item_wrapper) {
$sku = $line_item_wrapper->line_item_label->value();
//...
Printing out the following I was able to see a protected "data" property for the wrapper object:
print_r($line_item_wrapper->commerce_product);
Then, I tried finding the getter method for the field_image property with the following:
print_r($line_item_wrapper->commerce_product->getPropertyInfo('field_image');
I ended up here with entity_metadata_field_verbatim_get() but I don't know what parameters to pass. Also, in the last print statement above I didn't see anything else of value.
I'm wondering if I need to query for this data, and what table / columns to query for? Or maybe use something like node_load()? However, i'm not finding it too easy to find the node ID from the line item wrapper.
I was able to solve the above problem with this code:
$order = commerce_cart_order_load($user->uid);
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$result = array();
foreach($wrapper->commerce_line_items as $d => $line_item_wrapper) {
$product = array();
$product['quantity'] = $line_item_wrapper->quantity->value();
$product['sku'] = $line_item_wrapper->line_item_label->value();
$product['path'] = $line_item_wrapper->commerce_display_path->value();
$product['price'] = $line_item_wrapper->commerce_unit_price->amount->value();
$product['title'] = $line_item_wrapper->commerce_product->title->value();
$product['image'] = $line_item_wrapper->commerce_product->field_image->value();
$product['image'] = $product['image'][0]['filename'];
array_push($result, $product);
}
I have to say, finding all these discrete functions was quite difficult. I did not find any clear documentation about Drupal Commerce metadata_wrappers. If anyone could provide further information I'd love to take a look.
In general, I used a lot of print_r(); to find these values.
I need to gather all of the available attributes for the given product and then create a multidimensional array with them. Hopefully you can create a multidimensional array with more than two dimensions? The resulting array declarations should look like this:
$simpleArray[$child->getVendor()][$child->getColor()]=$child->getPrice();
First I'm gathering all the attributes then adding them to a string where I can call each one later:
$_product = $this->getProduct();
$_attributes = Mage::helper('core')->decorateArray($this->getAllowAttributes());
//Gather all attribute labels for given product
foreach($_attributes as $_attribute){
$attributeString .= '[$child -> get' . ucfirst($_attribute->getLabel()) . '()]';
}
Then I'm attempting to append that string to the array to declare it:
foreach($childProducts as $child) { //cycle through simple products to find applicable
//CAITLIN you are going to need way to search for other attributes, GET list of attributes
$simpleArray. $attributeString =$child->getPrice();
}
Mage::log('The attributeString is '. $simpleArray. $attributeString, null, 'caitlin.log'); //This is logging as "The attributeString is Array74"
Any suggestions?
You'll need to use recursion to do what you're requesting without knowing the attribute names while writing the code.
This will loop through and provide all of the child product prices, in a multi dimensional array based on the configurable attributes. It assumes that $_product is the current product.
$attrs = $_product->getTypeInstance(true)->getConfigurableAttributesAsArray($_product);
$map = array();
foreach($attrs as $attr) {
$map[] = $attr['attribute_code'];
}
$childPricing = array();
$childProducts = $_product->getTypeInstance()->getUsedProducts();
foreach($childProducts as $_child) {
// not all of the child's attributes are accessible, unless we properly load the full product
$_child = Mage::getModel('catalog/product')->load($_child->getId());
$topLevel = array($child->getData($map[sizeof($map)]) => $_child->getPrice());
array_pop($map);
$childProducts = array_merge($childProducts,$this->workThroughAttrMap($map,$_child,$topLevel));
}
//print_r childProducts to test, later do whatever you were originally planning with it.
In the same controller include this:
protected function workThroughAttrMap(&$map,$child,$topLevel) {
$topLevel = array($child->getData($map[sizeof($map)]) => $topLevel);
array_pop($map);
if(sizeof($map) > 0) return workThroughAttrMap($map,$child,$topLevel);
else return $topLevel;
}
I haven't tested this code so there may be a few minor bugs.
There are a few things you could do to make the code a bit cleaner, such as moving the first $topLevel code into the function, making that an optional parameter and initializing it with the price when it doesn't exist. I also haven't included any error checking (if the product isn't configurable, the child product doesn't have its price set, etc).
i am writing a magento product exporter, that writes a couple of attributes into a csv file. one attribute is called the "category string" and its method looks like:
...
foreach($products as $_product) {
...
$productId = $_product->getSku();
$productCategory = getCategoryString($_product['category_ids']);
...
}
...
function getCategoryString($numbers) {
$catString = '';
$catModel = Mage::getModel('catalog/category')->setStoreId(Mage_Core_Model_App::ADMIN_STORE_ID);
$ex = explode(',', $numbers);
foreach ($ex as $i => $e) {
if ($i > 0) {
$catString .= $catModel->load($e)->getName();
if ($i < (count($ex)-1))
$catString .= ' > ';
}
}
$ex = NULL;
$numbers = NULL;
$catModel->unsetData();
unset($catModel);
$catModel = NULL;
return $catString;
}
but after each iteration the method call costs about 1MB for each product and i have about 9000 products! i cannot clean up the $catModel variable! the $catModel = NULL and unset($catModel) lines have no effects. what am i doing wrong? how can i force to unset the object?!
We had the same problem with a cron for Magento, I know that it isn't the best way to do it but we needed to do it quickly.
Our solution was creating a new PHP file with the necessary code to do one single operation. From magento we get a product list and then call with exec() to this external PHP file product by product.
Something like this:
foreach($products as $_product) {
...
exec("do_the_work.php {$_product->getSku()}");
...
}
Hope it helps.
so your script would be so much cooler if you
get all catalog ids from all the products you have as an array
load the category collection with IN() clause with the category names you need
foreach your product collection one more time and select the neccecary category id's from preloaded collection with just the right elements
profit $$$$ cause you are not using memory like its unlimited for you
much much cooler thing would be joining the category names directly to product collection, that would consume even less resources