I have created a extension that filters product collection based on a attribute.
Below is the controllers, block and view template code.
Controller
$url = Mage::getUrl('no-route');
if(Mage::app()->getRequest()->getParam('ajax')){
echo $this->getLayout()->createBlock('catalogextensions/bestsellers_home_list')
->setTemplate('catalog/landings/bestseller.phtml')
->toHtml();
}
else{
$this->loadLayout();
$this->getLayout()->getBlock('head')->setTitle('Besesellers');
$this->renderLayout();
}
Block product collection function
$storeId = Mage::app()->getStore()->getId();
$products = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('*')
->addAttributeToFilter(array( array( 'attribute'=>'top_seller', 'eq' => '1' )));
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products);
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($products);
$products->getSelect()->limit(4,$this->get_cur_page());
return $products ;
Product collection is iterated on view.phtml template.
Now, it is taking 35 seconds to get the output, below are the statistics of execution that i was able to get by making use of PHP's microtime() function
For
Block(product collection ) it taking around 0.01 second
Template Rendering its taking around 0.12 second
But for controller function it is taking around 35 second
I am not able to find what to check, because controller function just create a Block at runtime.
*Note:I am making use of a Paid full page cache Extension "Mirasvit FPC".
how can i find it what is taking time
Server configuration is
30GB Ram with 4 vCPU.
Application server : Nginx + php5-fpm.
Version: Magento CE 1.8.0.1
Thanks.
You use a bit deprecated way to call the filters. I don't know if that can really influate the time of loading but using addFieldToFilter() would limit the calls to database and can be usefull. You can use :
Magento: Filter products by Status
filtering product collection on "is_salable"
The block is maybe not cached correctly. I mean the products are load each time the page is called.
Make sure your flat product is set to on and all attributes needed for your filters are set as filter.
If a listing comes from EAv tables it might consume lots of resources thus slowing down.
Additionally apply optimization tricks.
First, are you sure you need to add all attributes to select?
You can replace the
addAttributeToSelect('*')
with
addAttributeToSelect(array('attribute_code', 'attribute_code'))
Next, what exactly is that
$this->get_cur_page()
doing? Rest of code shouldn't make disastrous performance issues.
PS
This should have been a comment, but I'm unable to comment yet.
Related
Hello and good day to all the members of this great community. I'm still new in PHP and especially in Magento.
I'm not posting, waiting for answers, and leaving without replying back. This is a learning process. I hope to get a great support from all of you.
I have a product. I did create custom option for the product, that is an attribute named "a_size". The attribute has value of S, M and L. Each of the value has quantity.
In the single product view, I would like to call all the available size. That is the size (S, M, or L) that has quantity more than 0. I just want to show the available size, not how much the size left.
Can anybody guide me? I'm using Magento 1.7.x and as far for this 2 weeks, I did try pretty many of suggested answers from the community thru the search function.
The replies will be much appreciated. Thank you.
There are a few things to try.
Firstly check that when you set up your new attribute in the Magento Admin (Catalog->Attributes->Manage Attribute) that in the Frontend Properties box you have set Visible on Product View Page on Front-end to yes.
To get size values I use this code:
$cabac_sizeAttribute = $_product->getAttributeText("a_size");
but I have other code for getting attribute values that goes like this:
$_product_helper = Mage::helper('catalog/output');
$temp = $_product_helper->productAttribute($_product, $_product->getASize(), 'a_size');
I think it is related to the type of attribute: text, dropdown, multiselect etc so try both and see how you get on. But really the function productAttribute() is just applying formatting. You can read the function in the file app/core/Mage/Catalog/Helper/Output.php
Also, I wonder, if you have set up a configurable product and you are on the product view page then you will be viewing the configurable product. That product won't have an a_size value: you are trying to access the a_size attribute of the simple products that make up the configurable product, yes? Everything I wrote above is (I think) correct but to get the attribute of the simple products that are part of a configured product you should study the code in the function getJsonConfig() of the file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php
And in particular to these lines:
//file: file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php
//class: Mage_Catalog_Block_Product_View_Type_Configurable
//function: getJsonConfig()
foreach ($this->getAllowProducts() as $product) {
$productId = $product->getId();
foreach ($this->getAllowAttributes() as $attribute) {
$productAttribute = $attribute->getProductAttribute();
$productAttributeId = $productAttribute->getId();
$attributeValue = $product->getData($productAttribute->getAttributeCode());
Being careful about variable naming: $product is local here, I suggest changing it, and about $this - but if you are in a .phtml of the product view for configurables then I think your $this is already Mage_Catalog_Block_Product_View_Type_Configurable
Welcome to Magento coding. You are doing well; it is a long but rewarding path. (hints: local.xml is your vital friend and so is Alan Storm if you haven't come across his content yet.)
[Additionally, (welcome to Magento) I think you are trying to say eg S and L are out of stock and M is in stock but actually the function getAllowProducts() will disallow a product with zero stock and exclude it from the returned object. You will need to use
$allProducts = $this->getProduct()->getTypeInstance(true)
->getUsedProducts(null, $this->getProduct());
(taken from function getAllowProducts() in file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php)
and then, if needed, check that each product is allowed to be shown eg status=ENABLED, and then check its stock level...
]
Malachy.
If you want to get the values of your drop down attribute use the following code
$_product->getASize();
and initially load the product object
It seems like I have to set a store view before I can update an attribute on website scope – is that correct?
My code:
Mage::app('admin');
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$product = Mage::getModel('catalog/product');
$product->load(123);
$product->setStoreId('1'); // without this line the attribute is not updated
$product->setSomeattribute("abc");
$product->save();
Yes. that's correct. This is for performance reasons on the frontend. Usually you don't save products from frontend. See a detailed explanation of why is this needed.
But you don't need to do that. I's slow and resource consuming. Try to save it like this:
Mage::getSingleton('catalog/product_action')
->updateAttributes(array(123), array('somattribute'=>'abc'), 1);
The first parameter is and array with the product ids.
The second is an array with the attribute codes and values to be updated.
The third is the store id for which the update is done.
This method is faster.
I know how to limit fields when dealing with a collection, but what I'm wondering is if I can limit the selected fields when using getModel()->load($id).
My thought process is if I know the ID I use:
Mage::getModel('model')->load($id)
as opposed to:
Mage::getModel('model')->getCollection()->addFieldToFilter('id', $id)->getFirstItem();
The issue is load($id) returns everything. I know I can use addFieldToSelect on the collection. Is there an equivalent when using load()? When I google this I get the collection method.
Thanks,
GG
EDIT:
I just want to add that if the collection way is how it's done than that's fine. I just want to make sure it can't be done using load().
No, there is no alternative to use the collection except for rewriting the model code altogether which is probably not what you want to do.
The model load call can only be used to load a full entry - what you can specify is the attribute which to use for the load. For example:
Mage::getModel('catalog/product')->load($sku,'sku')
will load a product by its sku.
In fact the model load itself simply defers to it's resource by doing:
$this->_getResource()->load($this, $id, $field);
But the resource load method does also not provide the ability to filter for specific attributes, so you will always load the full pack or use the collection.
You could try loading a collection with only one item (as you are doing now), and selecting what attributes you want using:
addAttributeToSelect('you_attribute_code_here')
This will return a collection (albeit containing one item), with only the attributes you wish to have returned. Example:
$productIds = array(166);
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('description')
->addFieldToFilter('entity_id', array('in' => $productIds));
$product = $collection->getFirstItem();
today I'm fighting with Magento again :) and I found a difference between
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', $product_sku);
and
$product = Mage::getModel('catalog/product')->load($product_id);
Can anyone exaplain me a difference between these two approaches? I found that when I'm loading a product by sku then when I 'try to re-save it with changed data then I get error exception 'Varien_Exception' with message 'Invalid method Varien_Object::save in app\code\core\Mage\CatalogInventory\Model\Observer.php(153): Varien_Object->__call('save', Array) that's true because once you try to load by sku then another observer sets product's stock item as Varien_Object, that looks like pitfall or I just dont understand it enough, but
I do daily Magento development from its beginnig so I know a lot about system and this is new for me. Thanks in advance, Jaro.
Interesting. While both methods will net you a single product model instance with fully loaded EAV data (provided the third parameter of loadByAttribute() is not passed or is *), the observers which add stock-related data are different for products vs. product collections, yet both stock information objects are added to the product data key "stock_item". It's debatable, but this feels like a bug. I would think that Mage_CatalogInventory_Model_Observer::saveInventoryData() or Mage_CatalogInventory_Model_Observer::_prepareItemForSave() would handle this.
You could resolve this issue by setting the product stock item fully on your product instance using the stock_item object.
loadByAttribute is a serious misnomer in my opinion because it doesn't actually trigger a load(); rather it uses getResourceCollection():
public function loadByAttribute($attribute, $value, $additionalAttributes = '*')
{
$collection = $this->getResourceCollection()
->addAttributeToSelect($additionalAttributes)
->addAttributeToFilter($attribute, $value)
->setPage(1,1);
Because it doesn't trigger the observer events associated with load() it means the resulting product object doesn't include the full set of product data you might want. In my case I needed the "description" attribute and it wasn't included.
There are several ways to resolve this:
Use a different method to load by SKU:
$product = Mage::getModel("catalog/product");
$product->load($product->getIdBySku("whatever"));
Force the desired attribute data to be included in the default product resource data by visiting Magento Admin > Catalog > Attributes > Edit attribute > "Used in Product Listing" = "Yes" and then reindexing. You should then be able to use the attribute data (in the frontend, at least) using loadByAttribute().
See also https://magento.stackexchange.com/a/197286/18855
I'm working with Magento CE 1.6 in a project where we need an easy way to fill the movie info in DVDs and Blu-Rays products for a reseller ecommerce. I'm set to use the Rotten Tomatoes API wich seems very adequate for our purposes, but here's the thing: We don't want to have to input every single detail of the movie in the New Product dialog, oppositely, we want to fetch the info automatically using the movie name as hint (the API perfectly supports this). I though that we could achieve this by two means:
Having the administrator to enter only the names of the movies and
create and run periodically a script that fetches the rest of the
info with the API and updates the data directly in the DB. I've been
watching the DB changes when a product is saved and would'nt like to
do that.
Editing the Magento code to make the new product form auto fillable,
maybe with ajax, once a movie name is entered. Zend framework isn't
my strong and seems kind of hard too.
Am I seeing this problem from the rigth angle? Is there maybe another API? Or a Magento extension? Or another ecommerce?!
I would suggest a little different approach. Enhancing the Admin interface is difficult, but possible. Here is an easier way.
Method #1 - Quick and Easy
Create yourself a script that would go through a list of products. You can select them by attribute type, category, or even just select them all! Then, loop through that collection, and for each product, grab the title, query the movie API, and set the product's attributes. Then, save the product, and move to the next one. Something like this:
Note: Be sure to create your custom attributes in the admin and assign them to the attribute set.
<?php
require_once 'app/Mage.php';
umask(0);
Mage::app('default');
function getVideoDataFromAPI($title)
{
// get your data from the API here...
return $data;
}
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToFilter('attribute_set_id', $yourAttributeSetId)
->addAttributeToFilter('year', ''); // <-- Set a field here that will be empty by default, and filled by the API. This is '' because it won't be null.
foreach ( $collection->getAllIds() as $id ) {
$product = Mage::getModel('catalog/product')->load($id);
$videoData = getVideoDataFromAPI($product->getName());
if ( empty($videoData) ) { continue; }
$product->setYear($videoData['year'])
->setRating($videoData['rating'])
->save();
}
?>
Method #2 - Do the above, but in a custom extension
I always like extensions over scripts. They are more secure and more powerful. With an extension you could have an admin list of the products, can filter them how ever you would like, and have a mass action to pull the video data manually. You could also set it up on a cron job to pull regularly.