views:

14286

answers:

7

In my block code I am trying to programatically 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?

+13  A: 

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 greater 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('name'=>'orig_price','eq'=>'Widget A'),
 array('name'=>'orig_price','eq'=>'Widget B'),  
));

foreach ($collection as $product) {
 //var_dump($product);
 var_dump($product->getData());
}
Alan Storm
very detailed answer. thanks!
Rick J
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 Thamer
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.
Alan Storm
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 Thamer
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.
Alan Storm
+1  A: 

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 Thamer
A: 

Christian -

I am trying to implement the code follow-up you posted, but I keep getting "undefined" errors regarding the arrays in each function.

I'm also not sure, because I'm pretty much a newbie to some of this, if I am calling the functions properly.

Is it possible for you to post an example of how you are calling these functions and what values you are passing to them?

Jason Nolley
I am calling the first function with the attribute code. You can verify you are using the correct attribute code but looking it up under the particular attribute in the magento admin.So for example in one instance my function call looks like this:$locationIds = getAttributeOptions('front_or_rear');
Christian Thamer
The second function call might look like: $products = getProductsByAttributeSetId(30);30 being the attribute set id.
Christian Thamer
Jason, I forgot to mention you should have commented on my answer rather than posting an answer of your own considering your answer is actually a question.
Christian Thamer
A: 

I'm not a coder but interested in doing this. Can you give me the steps on where and how to implement this? I presume I copy and paste the code and modified the attributes that I'm searching for, right? Where do place these codes at?

newguy
A: 

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

__('Release Date: %s', $this->dateFormat($_product->getReleasedate())) ?>

Now it is working in Magento 1.4.x

Anita Mourya
A: 

Great post, thanks!

I'm able to get the id's but when l run getProductsByAttributeSetId(28) for example l don't get anything returned, why isn't it sending me an array of the product(s) that have this specific attribute value?

Jeff
A: 
Pragnesh Karia