78

In my block code I am trying to programmatically retrieve a list of products that have a attribute with a specific value.

Alternately if that is not possible how would one retrieve all products then filter them to just list the products with a specific attribute?

How would I perform a search using standard boolean filters AND or OR to match a subset of my products?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Christian
  • 1,272
  • 3
  • 13
  • 15

6 Answers6

160

Almost all Magento Models have a corresponding Collection object that can be used to fetch multiple instances of a Model.

To instantiate a Product collection, do the following

$collection = Mage::getModel('catalog/product')->getCollection();

Products are a Magento EAV style Model, so you'll need to add on any additional attributes that you want to return.

$collection = Mage::getModel('catalog/product')->getCollection();

//fetch name and orig_price into data
$collection->addAttributeToSelect('name');  
$collection->addAttributeToSelect('orig_price');    

There's multiple syntaxes for setting filters on collections. I always use the verbose one below, but you might want to inspect the Magento source for additional ways the filtering methods can be used.

The following shows how to filter by a range of values (greater than AND less than)

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToSelect('name');  
$collection->addAttributeToSelect('orig_price');    

//filter for products whose orig_price is greater than (gt) 100
$collection->addFieldToFilter(array(
    array('attribute'=>'orig_price','gt'=>'100'),
)); 

//AND filter for products whose orig_price is less than (lt) 130
$collection->addFieldToFilter(array(
    array('attribute'=>'orig_price','lt'=>'130'),
));

While this will filter by a name that equals one thing OR another.

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToSelect('name');  
$collection->addAttributeToSelect('orig_price');    

//filter for products who name is equal (eq) to Widget A, or equal (eq) to Widget B
$collection->addFieldToFilter(array(
    array('attribute'=>'name','eq'=>'Widget A'),
    array('attribute'=>'name','eq'=>'Widget B'),        
));

A full list of the supported short conditionals (eq,lt, etc.) can be found in the _getConditionSql method in lib/Varien/Data/Collection/Db.php

Finally, all Magento collections may be iterated over (the base collection class implements on of the the iterator interfaces). This is how you'll grab your products once filters are set.

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToSelect('name');  
$collection->addAttributeToSelect('orig_price');    

//filter for products who name is equal (eq) to Widget A, or equal (eq) to Widget B
$collection->addFieldToFilter(array(
    array('attribute'=>'name','eq'=>'Widget A'),
    array('attribute'=>'name','eq'=>'Widget B'),        
));

foreach ($collection as $product) {
    //var_dump($product);
    var_dump($product->getData());
}
Andy
  • 4,901
  • 5
  • 35
  • 57
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • Thank you very much for the detailed answer. You have set me along the right path. I did a var_dump of the results from your example code. Because the attribute I am working with is a multiple select item I am getting a numeric id in the results so a text comparison is not working. E.G. $this->collection->addFieldToFilter(array( array('attribute'=>'cw_category','eq'=>'Aero'), array('attribute'=>'cw_category','eq'=>'Track'), array('attribute'=>'cw_category','eq'=>'Touring') )); Is returning 'cw_category' => string ',536,535,534' (length=12) – Christian Aug 27 '09 at 08:01
  • Can't specifically help you there without a lot of digging (StackOverflow rep is nice, but it doesn't pay the bills). Two avenues for you to pursue. First, as mentioned, checkout _getConditionSql for a list of all the possible comparison operators. You might be able to get by with a like clause or maybe an in. Secondly, if you checkout the PHPDoc for the addAttributeToFilter method on Mage_Eav_Model_Entity_Collection_Abstract, you'll see that one of the expected values of the first param is a Mage_Eav_Model_Entity_Attribute_Interface. That might lead you on the correct path. – Alana Storm Aug 27 '09 at 17:39
  • Alan, thanks for the additional tips. I went through the code for hours and had no luck. I did learn a lot more about Magento in the process though so I guess it isn't all bad. I managed to get the sql debugger working. It looks like for now I am going to just use the attribute ID in my filtering criteria. e.g. ...->addAttributeToFilter('cw_category','536') instead of ->addAttributeToFilter('cw_category','Aero') for example. – Christian Aug 28 '09 at 05:18
  • I've seen that as a common way of handling it. I think the way you're "supposed" to do this is instantiate and attribute object of the type you want, and pass that to one of the filtering methods. – Alana Storm Aug 28 '09 at 18:54
  • 1
    In the final code sample, your arrays start with `'name' => 'orig_price'` - is this correct? Shouldn't it be `'attribute' => 'name'`? – Andy Jan 31 '18 at 10:54
  • 1
    @johnsnails that's very possible, this post in 9 years old. My answer may have included inaccurate information, or the API may have changed out from under it. I don't do enough Magento work these days to know for sure. – Alana Storm Oct 16 '18 at 15:40
  • 1
    @AlanStorm It looks like it was just a copy/paste mistake from another code sample in your answer. I'm going to edit the answer and rectify it. – Andy Oct 18 '18 at 13:08
7

This is a follow up to my original question to help out others with the same problem. If you need to filter by an attribute, rather than manually looking up the id you can use the following code to retrieve all the id, value pairs for an attribute. The data is returned as an array with the attribute name as the key.

function getAttributeOptions($attributeName) {
    $product = Mage::getModel('catalog/product');
    $collection = Mage::getResourceModel('eav/entity_attribute_collection')
              ->setEntityTypeFilter($product->getResource()->getTypeId())
              ->addFieldToFilter('attribute_code', $attributeName);

    $_attribute = $collection->getFirstItem()->setEntity($product->getResource());
    $attribute_options  = $_attribute->getSource()->getAllOptions(false);
    foreach($attribute_options as $val) {
        $attrList[$val['label']] = $val['value'];
    }   

    return $attrList;
}

Here is a function you can use to get products by their attribute set id. Retrieved using the previous function.

function getProductsByAttributeSetId($attributeSetId) {
   $products = Mage::getModel('catalog/product')->getCollection();
   $products->addAttributeToFilter('attribute_set_id',$attributeSetId);

   $products->addAttributeToSelect('*');

   $products->load();
   foreach($products as $val) {
     $productsArray[] = $val->getData();
  }

  return $productsArray;
}
Christian
  • 1,272
  • 3
  • 13
  • 15
5
$attribute = Mage::getModel('eav/entity_attribute')
                ->loadByCode('catalog_product', 'manufacturer');

$valuesCollection = Mage::getResourceModel('eav/entity_attribute_option_collection')
            ->setAttributeFilter($attribute->getData('attribute_id'))
            ->setStoreFilter(0, false);

$preparedManufacturers = array();            
foreach($valuesCollection as $value) {
    $preparedManufacturers[$value->getOptionId()] = $value->getValue();
}   


if (count($preparedManufacturers)) {
    echo "<h2>Manufacturers</h2><ul>";
    foreach($preparedManufacturers as $optionId => $value) {
        $products = Mage::getModel('catalog/product')->getCollection();
        $products->addAttributeToSelect('manufacturer');
        $products->addFieldToFilter(array(
            array('attribute'=>'manufacturer', 'eq'=> $optionId,          
        ));

        echo "<li>" . $value . " - (" . $optionId . ") - (Products: ".count($products).")</li>";
    }
    echo "</ul>";
}
verheesj
  • 1,438
  • 2
  • 16
  • 24
3

To Get TEXT attributes added from admin to front end on product listing page.

Thanks Anita Mourya

I have found there is two methods. Let say product attribute called "na_author" is added from backend as text field.

METHOD 1

on list.phtml

<?php $i=0; foreach ($_productCollection as $_product): ?>

FOR EACH PRODUCT LOAD BY SKU AND GET ATTRIBUTE INSIDE FOREACH

<?php
$product = Mage::getModel('catalog/product')->loadByAttribute('sku',$_product->getSku());
$author = $product['na_author'];
?>

<?php
if($author!=""){echo "<br /><span class='home_book_author'>By ".$author ."</span>";} else{echo "";}
?>

METHOD 2

Mage/Catalog/Block/Product/List.phtml OVER RIDE and set in 'local folder'

i.e. Copy From

Mage/Catalog/Block/Product/List.phtml

and PASTE TO

app/code/local/Mage/Catalog/Block/Product/List.phtml

change the function by adding 2 lines shown in bold below.

protected function _getProductCollection()
{
       if (is_null($this->_productCollection)) {
           $layer = Mage::getSingleton('catalog/layer');
           /* @var $layer Mage_Catalog_Model_Layer */
           if ($this->getShowRootCategory()) {
               $this->setCategoryId(Mage::app()->getStore()->getRootCategoryId());
           }

           // if this is a product view page
           if (Mage::registry('product')) {
               // get collection of categories this product is associated with
               $categories = Mage::registry('product')->getCategoryCollection()
                   ->setPage(1, 1)
                   ->load();
               // if the product is associated with any category
               if ($categories->count()) {
                   // show products from this category
                   $this->setCategoryId(current($categories->getIterator()));
               }
           }

           $origCategory = null;
           if ($this->getCategoryId()) {
               $category = Mage::getModel('catalog/category')->load($this->getCategoryId());

               if ($category->getId()) {
                   $origCategory = $layer->getCurrentCategory();
                   $layer->setCurrentCategory($category);
               }
           }
           $this->_productCollection = $layer->getProductCollection();

           $this->prepareSortableFieldsByCategory($layer->getCurrentCategory());

           if ($origCategory) {
               $layer->setCurrentCategory($origCategory);
           }
       }
       **//CMI-PK added na_author to filter on product listing page//
       $this->_productCollection->addAttributeToSelect('na_author');**
       return $this->_productCollection;

}

and you will be happy to see it....!!

gmadd
  • 1,146
  • 9
  • 18
Pragnesh Karia
  • 509
  • 2
  • 6
  • 14
2

create attribute name is "price_screen_tab_name". and access using this simple formula.

<?php $_product = $this->getProduct(); ?>
<?php echo $_product->getData('price_screen_tab_name');?>
Remees M Syde
  • 2,564
  • 1
  • 19
  • 42
Pratik Kamani
  • 758
  • 7
  • 23
0

I have added line

$this->_productCollection->addAttributeToSelect('releasedate');

in

app/code/core/Mage/Catalog/Block/Product/List.php on line 95

in function _getProductCollection()

and then call it in

app/design/frontend/default/hellopress/template/catalog/product/list.phtml

By writing code

<div><?php echo $this->__('Release Date: %s', $this->dateFormat($_product->getReleasedate())) ?>
</div>

Now it is working in Magento 1.4.x