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. :)
Related
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?
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();
}
For my module I need to create a custom productCollection built with various addAttributeToFilter modifiers. However, I have no idea how to get such a collection into a product list like the default magento one.
So basicly I'd like to create a pre-filtered product list, could anyone give me some advice on how to start such a thing?
EDIT: Just to clarify, I can make the collection, just not show it like the default product list.
After hours of struggling I found a solution:
I overrided Mage_Catalog_Block_Product_List and made my own _getProductCollection with:
$collection = parent::_getProductCollection();
$collection->addAttributeToFilter('attribute', array('operator' => 'value'));
/* more filters go here */
$this->_productCollection = $collection;
return $this->_productCollection;
This seemed to be the only way to get the original product list working without any errors or category problems.
With thanks to Guerra!!
use this in list.phtml:
$_productCollection->clear()
->addAttributeToFilter('attribute_set_id', array('eq' => 63))
->load();
You can try :
Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter();
http://www.magentocommerce.com/wiki/1_-_installation_and_configuration/using_collections_in_magento
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
I have Magento 1.3.2 and I have weird issue:
When I am in list.phtml, and I try to fetch the SKU by using getSku() or getData('sku') I get empty string. getName() does work. However, when I do that from other pages, it works well.
I var_dump-ed it and no SKU is shown.
What can cause this?
I'm surprised nobody has given you the easiest and most proper answer yet:
Go to your admin, Catalog >> Attributes >> Manage Attributes. Then edit the 'sku' attribute. Change the "Used in Product Listing" from 'No' to 'Yes'. You will then have access to it from the product object in list.phtml with ->getSku()
The other option is to re-load the product object in the list.phtml using the ID of the product you already have. The code reads something a little like:
$sku = Mage::getModel('catalog/product')->load($_product->getId())->getSku();
Note that $_product is what you are getting in your collection already, and note that getSku is case sensitive (as are all Magento attributes getter/setters).
#Prattski's solution is preferable as you don't really want to be messing with loading/manipulating the objects, but it sounds as though your collection is a little messed up. SKU is one of the core fields that exists in the base catalog_product_entity table, so would be unusual not to be loaded.
Probably sku is not added to the list of attributes when a collection is retrieved. I assume you are talking a bout the file /template/catalog/product/list.phtml. If so, then you need to extend the corresponding code file (/app/code/core/Mage/Catalog/Block/Product/List.php).
I think your best bet is to overload the getLoadedProductCollection() method to:
public function getLoadedProductCollection()
{
return $this->_getProductCollection()->addAttributeToSelect('sku');
}
This might not work, I have not been able to test it, as in my store the sku and all other attributes are accessible in the list.phtml template file.
Try this:
<?php
$current_product = Mage::registry('current_product');
if($current_product) {
$sku = $current_product->getSku();
// output sku
echo $sku;
}
?>
I had same problem too but tried $_product['sku']
it works for me
$_product["sku"]; enough to get the product sku.