Magento addFieldToFilter:两个字段,匹配为OR而不是AND

45

我在这个问题上卡了几个小时。通过篡改/lib/Varien/Data/Collection/Db.php中的几行代码,我已经解决了它,但我更倾向于使用正确的解决方案并保持我的核心代码不变。

我只需要获取一个集合并按两个或更多字段进行过滤。比如说,customer_firstnameremote_ip。这是我的(在没有篡改Db.php的情况下无法正常工作的)代码:

$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)));

我使用了原始的Db.php,并尝试了以下操作:(示例取自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'),           
));

但是这会给我带来以下错误:

Warning: Illegal offset type in isset or empty  in magento/lib/Varien/Data/Collection/Db.php on line 369
如果我将其包装在try/catch中,那么它就会移动到_getConditionSql()并产生此错误:

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

有没有人有可用的、实用的代码来做这件事?我正在运行 Magento 1.9(企业版)。谢谢!

10个回答

78

我有另一种方法来添加一个 or 条件在字段中:

->addFieldToFilter(
    array('title', 'content'),
    array(
        array('like'=>'%$titlesearchtext%'), 
        array('like'=>'%$contentsearchtext%')
    )
)

我尝试了所有的答案,这是唯一一个对我起作用的答案。Anda的OR答案试图搜索一个名为“attribute”的属性,因此我一直收到未知列“attribute”的错误。其他解决方案具有正确的列名称,但没有值。这在SQL中给了我正确的结果:... WHERE (('first_attribute' = 'some value') OR ('second_attribute' = 'some value')) - Mageician
尝试这种方式,我得到了一个“列未找到:1054未知列'Array'在'where子句'中”的错误。 - zuzuleinen
1
请注意,此语法不正确于 addAttributeToFilter,有关语法,请参阅 https://dev59.com/6G435IYBdhLWcg3wohpT#5301457 - user3338098

27

可以这样生成OR条件:

$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
    )
);

这将生成类似以下的 SQL WHERE 条件:

... 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'))
    )

每个嵌套的数组(<condition>)都会生成另一组括号用于OR条件。


这个方法之所以有效,是因为它使用第一个参数中的键(字段数组)在第二个参数(条件数组)中查找该字段对应的值来使用。 我认为更清晰的做法是:$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
这个过滤器实际上适用于OR条件。谢谢! - Corgalore
请注意,此语法不适用于addAttributeToFilter,请参考stackoverflow.com/a/5301457/3338098获取正确的语法。 - user3338098

16

我也尝试获取field1 = 'a' OR field2 = 'b'

你的代码对我没用。

这是我的解决方案。

$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());

这个查询语句是:SELECT name, keywords FROM abc WHERE keywords like '%foo%' OR additional_keywords like '%bar%'

也许这不是“Magento的方式”,但我卡在这里已经五个小时了。

希望能对你有所帮助。


1
这也是我所知道的最佳解决方案。 - zuzuleinen
我一直在尝试让自定义资源模型的复杂查询正常工作,已经努力了几个小时了。最终这是有效的方法。谢谢。 - billmalarky

14

这是我在Enterprise 1.11中的解决方案(在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),
                    )
            );

这将产生以下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)))

9

要按多个属性进行过滤,请使用以下方法:

//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')
            )
        );

了解更多信息,请参考以下链接:http://docs.magentocommerce.com/Varien/Varien_Data/Varien_Data_Collection_Db.html#_getConditionSql


1
谢谢,Anda。但是这只要求两个字段中的一个,而不是两个都要。如果在查询末尾添加->getSelectSql(),它会给出"...WHERE(my_field1 ='my_value1')AND(my_field2 ='my_value2')"。我正在尝试获取“WHERE(myfield1 ='myvalue1')OR(myfield2 ='myvalue2')”。 - Gabriel H
3
这仅适用于EAV集合中的addAttributeToFilter。Riyazkhan的答案是正确的。 - rterrani

8

感谢Anda,您的发布非常有帮助!但是OR语句对我来说不太适用,我一直在收到错误提示:getCollection() "invalid argument supplied for foreach"

因此,这就是我的最终结果(请注意,在这种情况下,属性被指定了3次而不是2次):

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

addFieldToFilter 首先需要一个字段,然后是条件 -> 链接.


+1!Anda的解决方法对于OR条件是不正确的。 - leek

5

这里有点混淆,让我试着澄清一下:

假设你想要类似以下的 SQL 代码:

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')
)

为了实现这一点,您的集合需要按照以下格式进行格式化:
$collection = Mage::getModel('enterprise_invitation/invitation')->getCollection();

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

现在,为了查看这个的外观,你可以通过使用echo函数打印出该查询创建的内容。
echo $collection->getSelect()->__toString();

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()
        );
}

结果:

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

Source: http://alanstorm.com/magento_collections


2
要为集合创建简单的OR条件,请使用以下格式:

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

这将生成如下的SQL语句:
WHERE (((`status` = 'processing') OR (`status` = 'pending')))

2
这是真正的Magento方式:
    $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
                                )
                            )
                        );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接