How to filter Magento Loaded Collection? - magento

What is the best practice to add filter to loaded collection in Magento? I want to hide items that are out of stocks and last updated date is not within last 30 days.
I try to create a function to hide the products in front end, then I realize it won’t affect the total item counts loaded in pager. I discovered that _getProductCollection() might the method I can use however I can’t add filter onto it.
I’m still new to Magento so any guidances are highly appreciated.

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addFieldToFilter( 'status', '1' );
$collection->addFieldToFilter(
'created_at',
array(
'lteq'=>$last_day
)
);
$collection->addFieldToFilter(
'updated_at',
array(
'gteq'=>$first_day
)
);

you can filter the out of stock products from the colection from admin settings under system->configuration->catalof->invemtory
and set `
Display Out of Stock Products option to no
Hope this will help you
`

you can add filter in _getProductCollection() function in app\code\core\Mage\Catalog\Block\Product\List.php
from
> $this->_productCollection = $layer->getProductCollection();
to
$this->_productCollection = $layer->getProductCollection()->addAttributeToFilter('created_at', 'your_value'));
`

Related

Get model of custom category

We have added in our categories trees some attributes to the categories
Its posible to make a foreach Mage::getModel(custom categories) to get all the value of the custom categories?
You should be able to access those attributes but you will need to load the category first. Now just getting the custom attributes might be a little harder.
$cateogry = Mage::getModel('catalog/category')->load('category here');
//this could get you all attributes
$attributes = $category->getAttributes();
//this would get you just the two attributes that look custom above
$customAttributes = array(
'latitude' => $category->getLatitude(),
'longitude' =>$category->getLongitude(),
);
I hope this helps!

Magento: Display number of times a product has been sold in the Product Grid

I am looking to add a column to the product grid (in the admin area to be clear) to display how many times this product has been sold. Here is what I have so far after piecing together from several other posts:
In app/code/local/Namespace/Qtysold/Block/Adminhtml/Catalog/Product/Grid.php
<?php
class Namespace_Qtysold_Block_Adminhtml_Catalog_Product_Grid extends Mage_Adminhtml_Block_Catalog_Product_Grid
{
/* Overwritten to be able to add custom columns to the product grid. Normally
* one would overwrite the function _prepareCollection, but it won't work because
* you have to call parent::_prepareCollection() first to get the collection.
*
* But since parent::_prepareCollection() also finishes the collection, the
* joins and attributes to select added in the overwritten _prepareCollection()
* are 'forgotten'.
*
* By overwriting setCollection (which is called in parent::_prepareCollection()),
* we are able to add the join and/or attribute select in a proper way.
*
*/
public function setCollection($collection)
{
/* #var $collection Mage_Catalog_Model_Resource_Product_Collection */
$store = $this->_getStore();
if ($store->getId() && !isset($this->_joinAttributes['qty_sold'])) {
$collection->joinAttribute(
'qty_sold',
'reports/product_collection',
'entity_id',
null,
'left',
$store->getId()
);
}
else {
$collection->addAttributeToSelect('qty_sold');
}
echo "<pre>";
var_dump((string) $collection->getSelect());
echo "</pre>";
parent::setCollection($collection);
}
protected function _prepareColumns()
{
$store = $this->_getStore();
$this->addColumnAfter('qty_sold',
array(
'header'=> Mage::helper('catalog')->__('Qty Sold'),
'type' => 'number',
'index' => 'qty_sold',
),
'price'
);
return parent::_prepareColumns();
}
}
A couple of things here. 1) $store->getId() returns 0 so it never goes into that first block in setCollection, is that correct behavior since it is the Admin area? 2) If I force the joinAttribute to run, it causes an exception (Invalid entity...) which is sort of expected since reports doesn't appear to really be an entity, but I'm not really clear on this whole entity business. 3) In other examples (like this one: http://www.creativemediagroup.net/creative-media-web-services/magento-blog/30-show-quantity-sold-on-product-page-magento) they use something like this:
$_productCollection = Mage::getResourceModel('reports/product_collection')
->addOrderedQty($from, $to, true)
->addAttributeToFilter('sku', $sku)
->setOrder('ordered_qty', 'desc')
->getFirstItem();
And I am not sure if there is any way to "join" with this reports/product_collection or if there is any way to recreate its "addOrderedQty" data?
This is on Magento 1.7. I can provide further details as needed. I am a beginner with Magento development so any help at all (including resources to learn) would be greatly appreciated. Thanks!
1) Admin Area does have the store ID = 0, so yes this will always be returned.
this will also mean your conditional always fails and never does any joining, it will just try and add the qty_sold to the collection, which of course will not work as it's not part of that entities data.
The issue is the joinAttribute method will only work on "Entites" (it depends on the classes used by the models you are trying to join), and as the reports/product collection isn't one of these you will have to join another way using methods like this:
join() or joinLeft()
with this kind of thing:
$collection->getSelect()
->join(
'customer_entity',
'main_table.customer_id = customer_entity.entity_id',
array('customer_name' => 'email')
);
Was also dealing with this issue and solved it as follows:
protected function _prepareCollection() {
// [...]
$collection = Mage::getModel('catalog/product')->getCollection();
// Add subquery join to sales_flat_order_item to get a SUM of how many times this product has been ordered
$totalOrderedQuery = Mage::getSingleton('core/resource')->getConnection('core_read')
->select()
->from('sales_flat_order_item', array('product_id', 'qty_ordered' => 'SUM(`qty_ordered`)'))
->group('product_id');
$collection->joinField('qty_ordered', $totalOrderedQuery, 'qty_ordered', 'product_id=entity_id', null, 'left');
// [...]
return parent::_prepareCollection();
}
Note the usage of $collection->joinField(), tried using the mentioned $collection->getSelect()->join(), but it gives all sorts of issues with sort orders and filtering due to the internal Magento data collection not recognizing the column as part of the data set.
Then in _prepareColumns you can simply add the column:
$this->addColumn('qty_ordered', array(
'header' => Mage::helper('xyz')->__('Total quantity ordered'),
'sortable' => true,
'width' => '10%',
'index' => 'qty_ordered',
));
You might want to add a renderer which checks and converts a NULL value to '0', otherwise you will end up with empty columns if the product hasn't been ordered yet (you could also fix this in the SELECT query by using IFNULL for qty_ordered).
I was able to achieve what I needed (though it is hardly perfect) thanks to that tip from Andrew. Here is my updated code for setCollection:
public function setCollection($collection)
{
/* #var $collection Mage_Catalog_Model_Resource_Product_Collection */
$store = $this->_getStore();
$collection->getSelect()->joinLeft(
array('oi' => 'sales_flat_order_item'),
'e.entity_id = oi.product_id',
array("qty_sold" => 'SUM(oi.qty_ordered)')
)->group('e.entity_id');
parent::setCollection($collection);
}
I was curious if any Magento developers see a serious flaw in this approach (I'm aware of some of the business logic such as not counting products that were canceled, etc) or recommendations for a better approach. Either way this is "good enough" for now and meets my needs, so I'll post in case others need it.

Filtering products by attributes in magento

How do i list products with a specific attribute? for example if Size is an attribute in my default attribute set. Then how do i list all the products which has Size = 22?
I am searching google for the answer, will post as answer if i find any.
Thanks,
Balan
I just did this not too long ago
To get the store id:
$storeId = Mage::app()->getStore()->getId();
To get all the products filtering out everything but Size
$_productCollection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('*')
->addAttributeToFilter("Size", "1");
Then simply in a foreach loop add the following(this will loop through the array $_productCollection)
foreach($_productCollection as $_product)
{
$attr = Mage::getResourceModel('catalog/product')->getAttributeRawValue($_product->getId(), 'Size', $storeId);
if($attr == '22')
{
//Echo names of the products
echo $_product->getName();
}
}
This way you can still do a lot with the product list without having a strict listing of products. So you can still add names, prices, and more attributes if you wish. Hope this helped!
I don't think the catalog product is made for this. The best would be to move the attribute Size in the static table (catalog_product_entity). Then you will be able to search by size with keeping the catalog fast. After if you want to do this on different attribute, I guess you can give it as parameter in the collection. But I am not sure about it.
For example:
$storeId = Mage::app()->getStore()->getId();
$products = Mage::getResourceModel('catalog/product_collection');
$products->addAttributeToFilter(array(
array('attribute' => 'attribute_code', 'eq' => 22)))
->addAttributeToSelect('*')
->setStoreId($storeId)
->addStoreFilter($storeId)
->setOrder('name', 'desc');
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products);
$this->setProductCollection($products);
for me this works fine.

Update all Tier prices for a product

Without directly querying the Magento database. How can i remove all the tier prices for a certain product based on quantity and customer group?
Then add new tier prices for this product.
Example:
Remove all tier prices for a product with the SKU: FD001
where the customer group id is: 4
PHP 5.3
Magento 1.4.2
You first have to unset the tier prices and save the product, then add the tier price(s) and save again.
Mage::getModel("catalog/product")->load(123)
->unsTierPrice()
->save()
->setTierPrice(array(
array(
'website_id' => 0,
'all_groups' => 0,
'cust_group' => 4,
'price' => 99.99,
'price_qty' => 1
),
array() // another tier, etc
))
->save();
I ended up solving this by using direct database queries.
As always I'm looking for a better answer.
My Hacky solution:
$product = Mage::getModel('catalog/product')->load(9999);
$dbc = Mage::getSingleton('core/resource')->getConnection('core_write');
$dbc->query('DELETE FROM `catalog_product_entity_tier_price` WHERE `entity_id` = ' . intval($product->getId()) . ' AND `customer_group_id` IN(3,4,6,7) AND qty = 1');
$dbc->query(
'INSERT INTO `catalog_product_entity_tier_price` (`entity_id`,`all_groups`,`customer_group_id`,`qty`,`value`,`website_id`)
VALUES ('. intval($product->getId()) . ',0,' . intval($id) . ',1,' . floatval($price) . ',0)'
);
There's an API available for the product tier price.
Alternatively, you can use some PHP code that uses the Magento code to query for a list of products that match those criteria and then adjust each one. Alan Storm has an article about how Model Collections work (they're the type of object that you would use for this).
Basically it would be something like this to delete the products, I'm not sure how you would set the tier prices, but you can a look at the generated phpdoc documentation. I'm selecting every product that matches customer_group 4 and then deletes each product. You'll have to figure out how to filter things out based on the tier price...
<?php
require 'app/Mage.php';
$app = Mage::app('default'); // Mage_Core_Model_App
$store = $app->getStore(); // Mage_Core_Model_Store
$products = Mage::getModel('catalog/product')->getCollection();
// filter out anything that doesnt match the customer group 4
$products->addFieldToFilter('customer_group', '4');
// loop through each product and delete it
for ($products->load() as $product) {
$product->delete();
}
#omouse's answer pointed us to Magento's API for tier prices. [Updated Link for Magento 1.X's Tier Price API] Note: I'm working with Magento 1.9.2.2.
After looking around for the most efficient way to update a product's tier prices (without using direct SQL), I found the API is the fastest way. It's still slow, but it's better than the other methods I found which recommended doing something like:
// Works, but is slow
$product = Mage::getModel('catalog/product')->load($productId);
$product->unsTierPrice();
$product->save();
$product->load();
$product->setTierPrice($tiers);
$product->save();
Better, I made my array of tier arrays into an array of tier objects, and used the API functions to update the product:
// Build the array of tier level objects
foreach ($tierLevels as $tierLevel) {
$tiers[] = (object) array(
'website' => 'all',
'customer_group_id' => 'all',
'qty' => $tierLevel['min_qty'],
'price' => $tierLevel['unit_price']
);
}
// Use the API to do the update
try {
$tierPriceApi = Mage::getSingleton('catalog/product_attribute_tierprice_api_v2');
$tierPriceApi->update($productId, $tiers);
}
catch (Exception $e) {
// Handle the exception
}
I searched a while to find a solution, which is not pure SQL and does not need a product save, because we want to update tier prices for a catalog of 50k products, so saving all of them is surely not a solution.
I think I finally found a good way.
Mage::getResourceSingleton('catalog/product_attribute_backend_tierprice')
->deletePriceData($product->getId());
$newTierPrice['entity_id'] = $product->getId()
$newTierPrice['all_groups'] = 1;
$newTierPrice['customer_group_id'] = 0;
$newTierPrice['qty'] = 42;
$newTierPrice['value'] = 100;
$newTierPrice['website_id'] = 0;
Mage::getResourceSingleton('catalog/product_attribute_backend_tierprice')
->savePriceData(new Varien_Object($newTierPrice));

Magento - Retrieve highest product price in current collection from layered nav

Layered navigation filters are created in
app/design/frontend/base/theme/template/catalog/layer/filter.phtml
How can I retrieve the value of the highest product price in the current product collection from within this file?
I've tried what I thought was the obvious choice $this->getMaxPriceInt() from Mage_Catalog_Model_Layer_Filter_Price but that does not seem to work within filter.phtml.
Assuming $collection is a collection of 'catalog/product', this should do the trick:
$product = $collection->setOrder('price', 'DESC')->getFirstItem();
Get highest and lowest price in category listing you can use below code.
$min_price = Mage::getSingleton('catalog/layer')->setCurrentCategory(Mage::registry('current_category'))->getProductCollection()->getMinPrice();
$max_price = Mage::getSingleton('catalog/layer')->setCurrentCategory(Mage::registry('current_category'))->getProductCollection()->getMaxPrice();
But it is not working for search and advance search layer navigation.so you can use below code for all pages layer navigation.$min_price = $this->getLayer()->getProductCollection()->getMinPrice();
$max_price = $this->getLayer()->getProductCollection()->getMaxPrice();
You can use below code on app/design/frontend/THEME/default/template/catalog/layer/view.phtml file.
Could you please try this ( let me know the result )
$model = Mage::getModel('catalog/product');
$collection = $model->getCollection()
->addStoreFilter()
->addAttributeToSelect('price')
->addAttributeToSort('price', 'asc');
if(!emtpy($collection)):
foreach($collection as $products) {
echo $products->getPrice();
echo $products->getName();
}
endif;

Resources