Accessing image field of line items in Drupal 7 module - php

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.

Related

PHP - Pulling array and splitting the strings

Not sure how to title this properly but here's the issue I am running into currently. I built a cart and checkout system and it loads all the data into a database when it finalized the order. To save some space, I stored just the item IDs originally but then I ran into the issue of if I deleted the item from the database (because it was discontinued or whatever) then it wouldn't return the info I needed. And if they ordered more then 1 item the database record would be wrong. So I stored the data like so:
Itemid:Quantity:Price:name, itemid2:quantity2:price2:name2
OR
1:3:20.00:Flower Hat, 2:1:17.75:diamonds
The issue I have right now that I need help with is this. I need to seperate the four values into variables like $price, $item, $id, $ammount so I can display them on the order history page and I need to loop through all items on the array so I can print a row for each item with all four fields respective to that item.
I use strpos already to get the shipping info from the same database field which is formatted as METHOD:Price but since I have 3 :'s on my string I'm not sure how to go through each one. Thanks.
Here's a function
function parseItems($dbjunk){
$cart = array();
$items = explode(",",$dbjunk);
foreach($items as $i){
$chunks = explode(":", $i);
$cart[] = array(
"ItemID" => $chunks[0] ,
"Quantity" => $chunks[1] ,
"Price" => $chunks[2] ,
"name" => $chunks[3]
);
}
return $cart;
}
Example usage:
$dbjunk = "Itemid:Quantity:Price:name, itemid2:quantity2:price2:name2";
$parsed = parseItems($dbjunk);
print_r($parsed);
See: https://3v4l.org/rBkXF
If you need variables instead of an array you can use list(), like this..
$dbjunk = "Itemid:Quantity:Price:name, itemid2:quantity2:price2:name2";
$parsed = parseItems($dbjunk);
foreach($parsed as $p){
list($itemID, $Quantity, $Price, $name) = array_values($p);
var_dump($itemID, $Quantity, $Price, $name);
}
see: https://3v4l.org/l4vsn
You should not physically delete items from your database. Instead, put a new column named 'is_active' or something like that to indicate whether the product is active/non-deleted.
Answering your question, here is my suggestion:
$orderString = '1:3:20.00:Flower Hat, 2:1:17.75:diamonds';
$items = array();
foreach(explode(', ', $orderString) as $itemString) {
$itemData = explode(':', $itemString);
$items[] = array(
'id' => $itemData[0],
'amount' => $itemData[1],
'value' => $itemData[2],
'description' => $itemData[3]
);
}
with this code, you will obtain an array with the data of all the items in the string, no matter how much items are in the string
try something like
$data = 1:3:20.00:Flower Hat, 2:1:17.75:diamonds
list($price, $item, $uid, $id, $ammount) = explode(":", $data);
echo $user;
echo $item;
Read about First Normal Form. Basically, you want to store one value in one field. So, instead of this:
shipping = "method:price"
You want something like this:
shipping_method = "method"
shipping_price = "price"
Don't concern yourself with space -- it's essentially free nowadays.
Regarding your deleted items dilemma, your initial implementation was the way to go:
I stored just the item IDs originally
In addition to reverting to this technique, I would recommend two things:
Add a boolean field to your item table to represent if the item is currently available or not. This gives you the additional feature of being able to toggle items on/off without having to delete/insert records and change ids.
Before deleting an item, check to see if it's ever been ordered. If not, it's ok to delete. If so, instead just deactivate it.

Magento 1.9.X programmatically save custom attribute

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!

Magento gather attributes as multidimensional array keys

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).

Magento suppliers list.

Been trying to get this to work and can't work out if it is me, the code or the installation. Basically I am trying to get a list of all the suppliers in the system. Magento 1.4.0.1 is in use. I have tried to use the code at Magento Wiki but it just returns an empty array. As is or modified to use the "suplier" attribute. I don't seem to be able to get it to return anything. Can anyone point me in the right direction as to how to get the list?
try this code,it may be helpful to you
$attribute = Mage::getModel('eav/config')->getAttribute('catalog_product','supplier');
foreach ( $attribute->getSource()->getAllOptions(true, true) as $option )
{
echo $option['value'];
echo $option['label'];
}
Looks like only one way to do this. Dump the magento bits and just hit the database.
$magentoDb = Mage::getSingleton( 'core/resource' )->getConnection( 'core_write' );
$results = $magentoDb->fetchAll('SELECT DISTINCT(`value`) AS supplierName FROM `catalog_product_entity_varchar` WHERE `attribute_id` = 525 ORDER BY supplierName');
Gets the list as a straight array. Which can then be output as I want. The attribute id of 525 is from the eav_attribute table, no idea if it is going to be the same for all systems.

Invoicing a partial order; totals aren't updated

In our order proces it is possible to send an invoice for a partial order. So when a couple of order lines are being shipped, an invoice have to be send also.
To make this possible I use this code:
$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($items);
if (!$invoice->getTotalQty()) {
Mage::throwException(Mage::helper('core')->__('Cannot create an invoice without products.'));
}
$invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
$invoice->register();
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($invoice)
->addObject($invoice->getOrder());
$transactionSave->save();
$invoice->sendEmail();
$invoice->setEmailSent(true);
$invoice->save();
Where the $items variable is an array containing the order ids and the amount of products to be invoiced.
The created invoice shows the correct products to be invoiced, but somehow the totals aren't updated. The totals still are the totals of the complete order, instead of the partial invoice.
I probably have to update or recalculate the totals but can't find the right code to force the update.
Anyone around who can put me in the right direction?
Well, it seems I have found the problem. The functionality as described above works manually executing it in the administrator interface. The code as enclosed above I only got to work by changing a core file of Magento.
If you change line 103 of Mage_Sales_Model_Service_Order from continue; to $qty = 0; the functionality works.
In short, this is what happens. With continue the second row item isn't added to the invoice which the invoice makes thinks the curren item is the last item of the whole order and therefore needs to invoice the complete outstanding amount. In my case the invoice I did want to invoice and the row I didn't want to invoice.
I've submitted it as issue on the Magento issue list.
Today I faced with exactly this problem, but I found a more elegant way to solve it without editing the core. The solution is to pass the products that we don't want to invoice, with 0 quantity.
In this way, the code you changed in core will act exactly like in your solution :)
As an example if I have 2 products in my order:
array(
1234 => 1,
1235 => 2
)
passing this array:
$qtys = array(
1234 => 1,
1235 => 0
)
will force this code:
// Mage_Sales_Model_Service_Order: lines 97-103
if (isset($qtys[$orderItem->getId()])) { // here's the magic
$qty = (float) $qtys[$orderItem->getId()];
} elseif (!count($qtys)) {
$qty = $orderItem->getQtyToInvoice();
} else {
continue; // the line to edit according to previous solution
}
to act exactly like in your solution, so you don't have to edit core code.
Hope it helps :)
OK - took me a bit, but now I see how to correctly create the array.
foreach ($items as $itemId => $item) {
$itemQtyToShip = $item->getQtyToShip()*1;
if ($itemQtyToShip>0) {
$itemQtyOnHand = $stockItem->getQty()*1;
if ($itemQtyOnHand>0) {
//use the order item id as key
//set the amount to invoice for as the value
$toShip[$item->getId()] = $itemQtyToShip;
} else {
//if not shipping the item set the qty to 0
$toShip[$item->getId()] = 0;
}
}
$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($toShip);
This creates a proper invoice.

Categories