45

I've been stuck on this for the last few hours. I got it working by hacking a few lines in /lib/Varien/Data/Collection/Db.php, but I'd rather use the proper solution and leave my core untouched.

All I need to do is get a collection and filter it by two or more fields. Say, customer_firstname and remote_ip. Here's my (disfunctional without hacking Db.php) code:

$collection = Mage::getModel('sales/order')->getCollection()->
addAttributeToSelect("*")->
addFieldToFilter(array(array('remote_ip', array('eq'=>'127.0.0.1')),
array('customer_firstname', array('eq'=>'gabe'))), array('eq'=>array(1,2,3)));

With a stock Db.php, I tried this: (sample taken from http://magentoexpert.blogspot.com/2009/12/retrieve-products-with-specific.html)

$collection->addFieldToFilter(array(
    array('name'=>'orig_price','eq'=>'Widget A'),
    array('name'=>'orig_price','eq'=>'Widget B'),           
));

But that gives me this error:

Warning: Illegal offset type in isset or empty  in magento/lib/Varien/Data/Collection/Db.php on line 369

If I wrap that with a try/catch, then it moves into _getConditionSql() and gives this error:

Warning: Invalid argument supplied for foreach()  in magento/lib/Varien/Data/Collection/Db.php on line 412

Does anyone have any working, functional code for doing this? I'm running Magento 1.9 (Enterprise). Thanks!

dayuloli
  • 16,205
  • 16
  • 71
  • 126
Gabriel H
  • 451
  • 1
  • 4
  • 3

10 Answers10

78

I've got another way to add an or condition in the field:

->addFieldToFilter(
    array('title', 'content'),
    array(
        array('like'=>'%$titlesearchtext%'), 
        array('like'=>'%$contentsearchtext%')
    )
)
Jonathan Day
  • 18,519
  • 10
  • 84
  • 137
Riyazkhan
  • 797
  • 5
  • 2
  • I tried all the answers and this is the only answer that has worked for me. Anda's answer for the OR was trying to search for an attribute called 'attribute', so I kept getting unknown column 'attribute' errors. Other solutions had the right column names but no values. This gave me the correct results in the SQL: `... WHERE (('first_attribute' = 'some value') OR ('second_attribute' = 'some value'))` – Mageician May 29 '12 at 17:39
  • Trying this way I get an Column not found: 1054 Unknown column 'Array' in 'where clause'error – zuzuleinen Jul 23 '13 at 12:24
  • 1
    note that this syntax is not correct for addAttributeToFilter, refer to http://stackoverflow.com/a/5301457/3338098 for that syntax – user3338098 Jul 14 '16 at 17:14
27

OR conditions can be generated like this:

$collection->addFieldToFilter(
    array('field_1', 'field_2', 'field_3'), // columns
    array( // conditions
        array( // conditions for field_1
            array('in' => array('text_1', 'text_2', 'text_3')),
            array('like' => '%text')
        ),
        array('eq' => 'exact'), // condition for field 2
        array('in' => array('val_1', 'val_2')) // condition for field 3
    )
);

This will generate an SQL WHERE condition something like:

... WHERE (
         (field_1 IN ('text_1', 'text_2', 'text_3') OR field_1 LIKE '%text')
      OR (field_2 = 'exact')
      OR (field_3 IN ('val_1', 'val_2'))
    )

Each nested array(<condition>) generates another set of parentheses for an OR condition.

Darren Felton
  • 2,299
  • 1
  • 18
  • 18
CJ Dennis
  • 4,226
  • 2
  • 40
  • 69
  • The reason this works is that it uses the keys in the first param (the array of fields) to lookup the value in the second param (the array of conditions) to use for that field. I find it a little clearer to do something like: `$collection->addFieldToFilter( array('field_1'=>'field_1', 'field_2'=>'field_2'), array( 'field_1'=>array( array('in' => array('text_1', 'text_2', 'text_3'), array('like' => '%text') ), 'field_2'=>array('eq' => 'exact'), ) );` – Ariel Allon Nov 06 '14 at 18:52
  • This filter actually worked for OR conditions. Thanks! – Corgalore Jan 12 '15 at 18:28
  • note that this syntax is not correct for addAttributeToFilter, refer to stackoverflow.com/a/5301457/3338098 for that syntax – user3338098 Jul 14 '16 at 17:16
16

I also tried to get the field1 = 'a' OR field2 = 'b'

Your code didn't work for me.

Here is my solution

$results = Mage::getModel('xyz/abc')->getCollection();
$results->addFieldToSelect('name');
$results->addFieldToSelect('keywords');
$results->addOrder('name','ASC');
$results->setPageSize(5);

$results->getSelect()->where("keywords like '%foo%' or additional_keywords  like '%bar%'");

$results->load();

echo json_encode($results->toArray());

It gives me

SELECT name, keywords FROM abc WHERE keywords like '%foo%' OR additional_keywords like '%bar%'.

It is maybe not the "magento's way" but I was stuck 5 hours on that.

Hope it will help

Manashvi Birla
  • 2,837
  • 3
  • 14
  • 28
user654539
  • 169
  • 1
  • 2
14

Here is my solution in Enterprise 1.11 (should work in CE 1.6):

    $collection->addFieldToFilter('max_item_count',
                    array(
                        array('gteq' => 10),
                        array('null' => true),
                    )
            )
            ->addFieldToFilter('max_item_price',
                    array(
                        array('gteq' => 9.99),
                        array('null' => true),
                    )
            )
            ->addFieldToFilter('max_item_weight',
                    array(
                        array('gteq' => 1.5),
                        array('null' => true),
                    )
            );

Which results in this SQL:

    SELECT `main_table`.*
    FROM `shipping_method_entity` AS `main_table`
    WHERE (((max_item_count >= 10) OR (max_item_count IS NULL)))
      AND (((max_item_price >= 9.99) OR (max_item_price IS NULL)))
      AND (((max_item_weight >= 1.5) OR (max_item_weight IS NULL)))
Michael Payne
  • 534
  • 5
  • 13
9

To filter by multiple attributes use something like:

//for AND
    $collection = Mage::getModel('sales/order')->getCollection()
    ->addAttributeToSelect('*')
    ->addFieldToFilter('my_field1', 'my_value1')
    ->addFieldToFilter('my_field2', 'my_value2');

    echo $collection->getSelect()->__toString();

//for OR - please note 'attribute' is the key name and must remain the same, only replace //the value (my_field1, my_field2) with your attribute name


    $collection = Mage::getModel('sales/order')->getCollection()
        ->addAttributeToSelect('*')
        ->addFieldToFilter(
            array(
                array('attribute'=>'my_field1','eq'=>'my_value1'),
                array('attribute'=>'my_field2', 'eq'=>'my_value2')
            )
        );

For more information check: http://docs.magentocommerce.com/Varien/Varien_Data/Varien_Data_Collection_Db.html#_getConditionSql

Joseph at SwiftOtter
  • 4,276
  • 5
  • 37
  • 55
Anda B
  • 947
  • 7
  • 9
  • 1
    Thanks, Anda. But that's for requiring both fields, not one or the other. If you add ->getSelectSql() to the end of that query, it gives you "...WHERE (my_field1 = 'my_value1') AND (my_field2 = 'my_value2')". It's the "WHERE (myfield1 = 'myvalue1') OR (myfield2 = 'myvalue2')" that I'm trying to get. – Gabriel H Sep 30 '10 at 16:13
  • 3
    This only works with addAttributeToFilter in EAV collections. The Riyazkhan answer is the correct. – rterrani Aug 05 '12 at 15:54
8

Thanks Anda, your post has been a great help!! However the OR sentence didnt' quite work for me and I was getting an error: getCollection() "invalid argument supplied for foreach".

So this is what I ended with (notice the attribute being specified 3 times instead of 2 in this case):

  $collection->addFieldToFilter('attribute', array(  
    array('attribute'=>'my_field1','eq'=>'my_value1'),            
    array('attribute'=>'my_field2','eq'=>'my_value2') ));

addFieldToFilter first requires a field and then condition -> link.

jazkat
  • 5,600
  • 3
  • 25
  • 17
  • +1 for this. @[Anda's solution](http://stackoverflow.com/questions/3826474/magento-addfieldtofilter-two-fields-match-as-or-not-and/3828722#3828722) is not correct for an OR condition. – leek Jul 15 '11 at 19:15
5

There is a bit of confusion going on here, but let me try to clarify things:

Lets say you wanted sql that looked something like:

SELECT 
    `main_table`.*, 
    `main_table`.`email` AS `invitation_email`, 
    `main_table`.`group_id` AS `invitee_group_id` 
FROM 
    `enterprise_invitation` AS `main_table` 
WHERE (
    (status = 'new') 
    OR (customer_id = '1234')
)

In order to achieve this, your collection needs to be formatted like this:

$collection = Mage::getModel('enterprise_invitation/invitation')->getCollection();

$collection->addFieldToFilter(array('status', 'customer_id'), array(
array('status','eq'=>'new'),
array('customer_id', 'eq'=>'1234') ));

Now to see what this looks like you can always echo the query that this creates by using

echo $collection->getSelect()->__toString();
afuzzyllama
  • 6,538
  • 5
  • 47
  • 64
phpCodeNinja
  • 51
  • 1
  • 1
2

This is the real magento way:

    $collection=Mage::getModel('sales/order')
                ->getCollection()
                ->addFieldToFilter(
                        array(
                            'customer_firstname',//attribute_1 with key 0
                            'remote_ip',//attribute_2 with key 1
                        ),
                        array(
                            array('eq'=>'gabe'),//condition for attribute_1 with key 0
                            array('eq'=>'127.0.0.1'),//condition for attribute_2
                                )
                            )
                        );
Gabriel
  • 21
  • 1
2
public function testAction()
{
        $filter_a = array('like'=>'a%');
        $filter_b = array('like'=>'b%');
        echo(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('sku',array($filter_a,$filter_b))
        ->getSelect()
        );
}

Result:

WHERE (((e.sku like 'a%') or (e.sku like 'b%')))

Source: http://alanstorm.com/magento_collections

De Nguyen
  • 407
  • 4
  • 4
2

To create simple OR condition for collection, use format below:

    $orders = Mage::getModel('sales/order')->getResourceCollection();
    $orders->addFieldToFilter(
      'status',
      array(
        'processing',
        'pending',
      )
    );

This will produce SQL like this:

WHERE (((`status` = 'processing') OR (`status` = 'pending')))
XPS
  • 140
  • 1
  • 5