Magento Quote Item object - how can I load item's options? - php

While I know that I can just load Quote Item Options collection and filter it by item_id, I just can't wrap my mind around that Magento folks haven't added any _afterLoad or _loadOptions method to easily assign the options to an item, since there's already a _options, _optionsByCode properties...
to provide some example of what I'm doing:
// updated (forgot to actually load object
$item = Mage::getModel('sales/quote_item')->load($itemId);
$buyRequest = $item->getBuyRequest(); // almost empty, only qty is set
I want to know if there's some $item->loadOptions() method... or other native way to load options into an item
UPDATE
What I want to do: I want to load item object and use some native way to add its options to this loaded object.

I ended up using next piece of code
/** #var Mage_Sales_Model_Resource_Quote_Item_Option_Collection $options */
$options = Mage::getResourceModel('sales/quote_item_option_collection');
$options->addItemFilter($item->getId());
foreach ($options as $option) {
$item->addOption($option);
}
While I understand that it doesn't answer my question directly, I think there's just no method of loading options inside quote item.
So maybe someone will find the above piece of code useful.

Let's me share what I can do for my case to get cart item option, here is my piece of code:
foreach ($item->getOptions() as $option) {
if ($option->getCode() == 'info_buyRequest') {
$values = unserialize($option->getValue()); // to array object
// lookup custom option
if ($values && array_key_exists('options', $values)) {
//
}
}
}
As above code, I get all options of cart/quote item and lookup to find the code of info_buyRequest which stores the buying option of the item. My case, I was trying to look for the custom option of product that bought.
Note: You can check your item array data via: $item->getData() so that we can understand the structure of the data output.

In my case, I am adding additional options to the quote item in an observer. These options are not saved on the product in the backend as they are created dynamically.
In the cart, you can then simply use:
$quoteItem = someMethodToGetYourQuoteItem();
$quoteItem->getBuyRequest()->getYouroption();

Well right now you instantiate a new quote item object without any data. If you would load an actual quote item, you can use the function getOptions to retrieve all the options associated with the quote item.
What is it you want to do exactly?

Related

Omeka: Get all items in Controller

I'm writing an Omeka Plugin and wants to get the list of all public Items with their all elements, in a controller under my plugin.
I've tried get_items() but the function doesn't exists, it looks like the function is only available for the views - not sure how.
another try was to manually fetch the records from database, but that's not a standard way.
So, the question is, is there a predefined function/class or way to get all the items in a controller?
I'm not sure if there is a function that will get you the items along with all of their element texts, but if you want a list of items, inside the controller you should be able to make a call like:
$items = $this->_helper->db->getTable('Item')->findAll();
The Omeka docs warn against getting all the items at once because it could be memory intensive. So, alternatively, you can loop through items.
$items = $this->_helper->db->getTable('Item');
$item = $items->findFirst();
while($item != NULL){
// Do something
$item = $items->findNext($item);
}
There is a "public" property on an item that tells you if it's public. In order to get the element texts for an item, I think you'd have to make a query on the ElementText table.
For more information, see the Omeka read the docs page for Table_Item, Omeka_Db_Table and Item:
http://omeka.readthedocs.io/en/latest/Reference/libraries/Omeka/Db/Table.html
http://omeka.readthedocs.io/en/latest/Reference/models/Table/Item.html
http://omeka.readthedocs.io/en/latest/Reference/models/Item.html

Magento load product collection with all attributes

I need to iterate over some products and access custom attributes, my code is:
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addAttributeToFilter(array(array('attribute' => 'status', 'eq' => '1')))
// more filters
->load();
then I have to:
foreach ($collection as $product) {
// here I can't access custom attributes
$product = Mage::getModel('catalog/product')->load($product->getId());
// here I can access them
}
and my problem is when I have a lot of products my script spends a lot of time in foreach loop. Can I somehow speed this by loading collection with all attributes?
I suggest all your attributes should go in flat tables.
So do some attributes setting like searchable, available in front end listing.
Then do an indexing.
Now when you get product collection you will get needed attributes and then code from there.
Instead of a loop loading each product you need to load a collection of products.
If you are using flat-tables, you have to add your custom attributes to the flat index. You can add them in the config.xml of the according module.
You can than skip the productload in the foreach which is probably causing the long processing time.
What you are doing is not a good practice and should be avoided. You may try this
foreach($collection->getItems() as $item){
echo $item->getName(); //$item is product instance. you can use it
}
EDIT
In your question, you are doing a serious mistake and it should be avoided. That is what I am trying to point out through my answer.
See what you are doing here.
foreache($bigcollection as $singleone){
$product = Mage::getModel('catalog/product')->load($id);
}
Basically you are loading a product inside a big for loop. It is a big mistake. Because if you do this, Magento need to do "extra fetching works" from database. Do not use that method. Through my solution, I showed you how to avoid loading a product inside a forloop. It is much reliable method.
If you dont want to use forloop, I think you need to edit your question and put that requirement there.
If you need to avoid for loop, you need to go for flat table concepts. That is the only one way that I know. Otherwise you cannot avoid a for each loop. :)

Magento Adminhtml Grid using sales/order_item collection filtered by customer

My objective is simple. I have a module called Quotes, I have managed to get Magento to create a new quote record each time a cart is created by changing the is_active column when a quote is 'checked out'. So I have a bunch of quotes each related to a customer and i have sales/order_item rows each related to a quote.
I have a page in the backend which displays a grid of all the quotes. When a quote is clicked, the edit page has two tabs, one with a Form.php showing the details of the quote. (customer name, date etc), then I have another tab which should contain a grid of all the items in that quote. It seems so simple:
$this->addTab("items_section", array(
"label" => Mage::helper("quote")->__("Quote Items"),
"title" => Mage::helper("quote")->__("Quote Items"),
"content" => $this->getLayout()->createBlock("quote/adminhtml_quotes_edit_tab_cart")->toHtml(),
));
Then in my cart block I have this:
protected function _prepareCollection()
{
$collection = Mage::getModel('sales/order_item')->getCollection();
print_r($collection);
$this->setCollection($collection);
return parent::_prepareCollection();
}
I'm not even interested in loading the correct collection (by order_id) because there is a problem ro be solved here first: The print_r statement reveals the collection I specified but passing it to $this->setCollection($collection) gives me 'No records found' rendered in the grid. In typical Magento fashion there are no errors etc. I understand that the model is supposed to query the database as needed but that doesn't seem to be happening. I suppose it's time to read Mage::core files but you can imagine my frustration at such a simple tasking being so complicated so I would appreciate it if anyone who knows what's going on here can help me out. Thanks in advance.
I may be wrong, but you can't setCollection() on a quote with sales order items. It has to be populated with sales/quote model items.
I don't know what the scope of $this is in _prepareCollection() but i'm assuming since its on the cart block, its dealing with a quote.
Just a hint, you might instead of print_r($collection), try doing this...
echo "<pre>";
foreach ($collection as $item) {
var_dump($item->debug());
}
It supplies pretty much just the important info instead of the database structure. You can also check your item type and make sure you are using the correct model for your setCollection method. You can also throw a break; in there if you want to just get the first item etc. Debugging magento objects can be tedious, and i've found that this helps.
Our head developer managed to help me get it working. We're still not sure why but this seemed to work
protected function _prepareCollection()
{
$quoteId = $this->getRequest()->getParam('id');
$quote = Mage::getModel('sales/quote')->getCollection()->addFieldToFilter('entity_id', $quoteId);
if ($quote->getFirstItem()->getId()) {
$collection = $quote->getFirstItem()->getItemsCollection(false);
}
$this->setCollection($collection);
return parent::_prepareCollection();
}

Multi select filter in layered navigation

I have a custom multi select attribute which I'd like to take part in filtering of products. The attribute is set as used in Layered Navigation however doesn't appear in the list of available filters. Could be due to custom model implementation?
Anyone have some tips where to check why it doesn't appear? Attribute is set for several products
Magento version used is EE 1.11
Thanks
For those who will struggle with this in the future: the problem is in Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source file on line 191. By default multi select attribute values are being pulled from eav_attribute_option and if your custom attribute uses custom source model the attribute will not be indexed.
I don't know as of yet if it's intended but I couldn't find a better solution than overriding that model in local pull and adding required values in $options array.
Hope this helps someone, someday
What is the backend_type. i.e. are the values stored in the catalog_product_entity_varchar or catalog_product_entity_text table?
The backend_type has to match the checks in Mage_Catalog_Model_Resource_Eav_Attribute::isIndexable(), so text wouldn't work without rewriting the attribute model.
Is the is_filterable and/or is_filterable_in_search attribute property set?
The Mage_Catalog_Model_Product_Indexer_Eav::_registerCatalogAttributeSaveEvent() checks for those when updating the index for the layered navigation.
Are the methods getFlatColums(), getFlatIndexes() and getFlatUpdateSelect() implemented in the custom source model?
This actually is only required for building and updating the flat catalog product tables (so the used_in_product_listing or is_filterable property needs to be set in order for Magento to pick up the attribute).
Check the class Mage_Eav_Model_Entity_Attribute_Source_Table as a reference on what these there methods are supposed to return.
NOTE: I'm adding this in a new answer to use the code format.
How it was said, the problem is with multiselect attributes using a custom source model.
Solution:
Rewrite the class
Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source
Override the method:
_prepareMultiselectIndex
add this code after the $options array is filled with the default code (check line 200 in original file)
foreach($attrIds as $attId){
if( ! isset($options[$attId])){
$options[$attId] = $this->_getOptionsFromSourceModel($attId);
}
}
add this method too:
protected function _getOptionsFromSourceModel($attId)
{
$options = array();
/** #var Mage_Eav_Model_Entity_Attribute_Abstract $attribute */
$attribute = Mage::getResourceSingleton('catalog/product')->getAttribute($attId);
/** #var Mage_Eav_Model_Entity_Attribute_Source_Abstract $source */
$source = $attribute->getSource();
$sourceOptions = $source->getAllOptions();
if($sourceOptions){
foreach($sourceOptions as $sourceOption){
if(isset($sourceOption['value'])){
$options[$sourceOption['value']] = true;
}
}
}
return $options;
}
I couldn't find a less intrusive way to fix this.

looping through product collection and saving products is changing attribute values unintended changes

I am running a loop through a product collection like the following
$productCollection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('small_image')
->addAttributeToSelect('thumbnail')
->addAttributeToSelect('image')
->addAttributeToSelect('sku');
foreach($productCollection as $product){
$product->setSmallImage($product->getImage());
$product->setThumbnail($product->getImage());
$product->save();
}
I am setting the other image types to the same as the base image because the client forgot to set those in the import sheet. For some reason when doing this it is setting the visibility of all products to Catalog, Search. A lot of my products are suppose to be "Not Visible Individually", so of course this messes stuff up.
Any Idea why this value would change, and are there other values that might change based on how I looped through the product collection?
I think it is because i removed the addAttributeToSelect(*) and did it specific to the two attributes but when trying to do the product collection over 18k products it was crapping out
Ok so this is what I found. When saving an object in Magento, it will call the normal Model abstract save method, this then calls $this->_getResource()->save($this); which will call the entity abstract save method, which eventually gets to /app/code/core/Mage/Eav/Model/Entity/Abstract.php and its _beforeSave() method which calls the walkAttributes($partMethod, array $args=array()) method.
A long the way all the attributes are loaded the the Resource object of the entity. The walkAttributes method will go through all attributes associated to the entity and calls this method call_user_func_array(array($instance, $method), $args); and in my case it is calling the beforeSave method for an attribute, as you can see here
/app/code/core/Mage/Eav/Model/Entity/Attribute/Backend/Abstract.php
public function beforeSave($object)
{
$attrCode = $this->getAttribute()->getAttributeCode();
if (!$object->hasData($attrCode) && $this->getDefaultValue()) {
$object->setData($attrCode, $this->getDefaultValue());
}
}
So any attribute that does not have any data and has a default value, that default value is applied to the object, in sense overwriting my data that is really set just not loaded to my object. I am guessing the best way to loop over thousands of products is to set the pageSize to a number that your server can handle loading and then you can set addAttributeToSelect('*'). However I am not aware of the best way to loop over this correctly, so for me I might just load the product in the for loop and not set the attribute to select all.
Here is my attempt at looping over the whole collection, please post any improvements
$productCollection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('*')->setPageSize(200);
for ($i = 1; $i <= $productCollection->getLastPageNumber(); $i++) {
if ($productCollection->isLoaded()) {
$productCollection->clear();
$productCollection->setPage($i);
$productCollection->setPageSize(200);
}
foreach ($productCollection as $product) {
echo $product->getId() . "\n\n";
}
echo $i . "\n\n";
}
I would like to still hear any other input on this as to whether this is the right thinking or explanation of the problem.
I actually found a better way to loop over a large collection, which is to use the resource iterator model and here is a tutorial on how to do so from the Fontis guys
http://www.fontis.com.au/blog/magento/loading-large-collections

Categories