【问题标题】:Filtering Magento Collections by COND1 AND (COND2 OR COND3)按 COND1 AND (COND2 OR COND3) 过滤 Magento 集合
【发布时间】:2012-06-15 18:04:48
【问题描述】:

如何通过 attribute1 = value1 AND (attribute2 = value2 OR attribute3 = value2) 过滤 Magento 销售订单集合?我可以写 WHERE {COND1} AND {COND2} OR {COND3},但我不能组合 AND ({COND2} OR {COND3})

首先,这不是重复的 AFAIK,我见过 this,它在 1.3.2 版中运行良好,但在企业版 1.11.1 中不行。这就是我想要做的......获取在定义的日期范围内创建或更新的、状态为“处理”的 Magento 订单。这是在以前版本中有效的代码,但在我的版本中无效:

$orderIds = Mage::getModel('sales/order')->getCollection()
    ->addFieldToFilter('status', 'processing')
    ->addFieldToFilter(array(
        array(
            'attribute' => 'created_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
        array(
            'attribute' => 'updated_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
    ));

这是它生成的 SQL,以及由此产生的错误:

SELECT `main_table`.* FROM `sales_flat_order` AS `main_table`
WHERE (status = 'processing') AND (Array = '')

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Array' in 'where clause' 

在挖掘代码时,我在 lib/Varien/Data/Collection/Db.php 中找到了 addFieldToFilter 函数

/** 
 * Add field filter to collection
 *
 * @see self::_getConditionSql for $condition
 * @param string $field
 * @param null|string|array $condition
 * @return Mage_Eav_Model_Entity_Collection_Abstract
 */
public function addFieldToFilter($field, $condition=null)
{   
    $field = $this->_getMappedField($field);
    $this->_select->where($this->_getConditionSql($field, $condition),
        null, Varien_Db_Select::TYPE_CONDITION);
    return $this;
}   

// **********************************************
// ** Different from addFieldToFilter in 1.3.2 **
// **********************************************

/** 
 * Add field filter to collection
 *
 * If $attribute is an array will add OR condition with following format:
 * array(
 *     array('attribute'=>'firstname', 'like'=>'test%'),
 *     array('attribute'=>'lastname', 'like'=>'test%'),
 * )
 *
 * @see self::_getConditionSql for $condition
 * @param string|array $attribute
 * @param null|string|array $condition
 * @return Mage_Eav_Model_Entity_Collection_Abstract
 */
public function addFieldToFilter($field, $condition=null)
{   
    $field = $this->_getMappedField($field);
    $this->_select->where($this->_getConditionSql($field, $condition));
    return $this;
}       

貌似addFieldToFilter以前是接受第一个参数为数组,但现在肯定是字符串了...有意思,于是零运气尝试了以下...

$orderIds = Mage::getModel('sales/order')->getCollection()
    ->addFieldToFilter('status', 'processing')
    ->addFieldToFilter('attribute', array(
        array(
            'attribute' => 'created_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
        array(
            'attribute' => 'updated_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
    ));
SELECT `main_table`.* FROM `sales_flat_order` AS `main_table`
WHERE (status = 'processing')
AND ((
    (attribute >= '2012-06-13 17:52:01' AND attribute <= '2012-06-15 17:52:01')
 OR (attribute >= '2012-06-13 17:52:01' AND attribute <= '2012-06-15 17:52:01')
))

我知道我可以通过操作 SQL 来做到这一点,但如果有的话,我真的很想知道如何以“Magento 方式”来做到这一点......

顺便说一句,我也尝试过使用addAttributeToFilter,错误消息是“无法确定字段名称”。


更新

我在Mage_Sales_Model_Resource_Order_Collection 中发现了两个看起来很有希望的函数,但它们仍然不是我想要的。

/**
 * Add field search filter to collection as OR condition
 *
 * @see self::_getConditionSql for $condition
 *
 * @param string $field
 * @param null|string|array $condition
 * @return Mage_Sales_Model_Resource_Order_Collection
 */
public function addFieldToSearchFilter($field, $condition = null)
{
    $field = $this->_getMappedField($field);
    $this->_select->orWhere($this->_getConditionSql($field, $condition));
    return $this;
}

/**
 * Specify collection select filter by attribute value
 *
 * @param array $attributes
 * @param array|integer|string|null $condition
 * @return Mage_Sales_Model_Resource_Order_Collection
 */
public function addAttributeToSearchFilter($attributes, $condition = null)
{
    if (is_array($attributes) && !empty($attributes)) {
        $this->_addAddressFields();

        $toFilterData = array();
        foreach ($attributes as $attribute) {
            $this->addFieldToSearchFilter($this->_attributeToField($attribute['attribute']), $attribute);
        }
    } else {
        $this->addAttributeToFilter($attributes, $condition);
    }

    return $this;
}

当我更新我的代码时,我得到非常接近我想要的结果,但是我真正想要的是有 CONDITION1 AND (CONDITION2 OR CONDITION3)

$orderIds = Mage::getModel('sales/order')->getCollection()
    ->addFieldToFilter('status', 'processing')
    ->addAttributeToSearchFilter(array(
        array(
            'attribute' => 'created_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
        array(
            'attribute' => 'updated_at',
            'from' => $fromDate->toString('yyyy-MM-dd HH:mm:ss'),
            'to'   => $toDate->toString('yyyy-MM-dd HH:mm:ss'),
        ),
    ));

SELECT `main_table`.*,
       `billing_o_a`.`firstname`,
       `billing_o_a`.`lastname`,
       `billing_o_a`.`telephone`,
       `billing_o_a`.`postcode`,
       `shipping_o_a`.`firstname`,
       `shipping_o_a`.`lastname`,
       `shipping_o_a`.`telephone`,
       `shipping_o_a`.`postcode`
FROM `sales_flat_order` AS `main_table`
LEFT JOIN `sales_flat_order_address` AS `billing_o_a`
    ON (main_table.entity_id = billing_o_a.parent_id AND billing_o_a.address_type = 'billing')
LEFT JOIN `sales_flat_order_address` AS `shipping_o_a`
    ON (main_table.entity_id = shipping_o_a.parent_id AND shipping_o_a.address_type = 'shipping')
WHERE (status = 'processing')
OR (created_at >= '2012-06-16 16:43:38' AND created_at <= '2012-06-18 16:43:38')
OR (updated_at >= '2012-06-16 16:43:38' AND updated_at <= '2012-06-18 16:43:38')

【问题讨论】:

  • 我不认为你可以这样做,因为sales/order 映射到一个平面表。改为在集合上使用 getSelect()。
  • @nevvermind 为什么它是否是平桌很重要?
  • @nevvermind 很好的参考。我在搜索中从未遇到过那个 Q。我仍然希望有办法做到这一点,但有趣的是 sales_order 表在 1.3.2 中是 EAV,现在是平面表。

标签: php mysql magento


【解决方案1】:

请注意,FLAT TABLE 和 EAV 集合使用不同的语法!

向 EAV 集合添加 OR 条件

请注意,当为同一属性指定 OR 条件时,如果您想使用两个不同的属性,则语法是不同的。

使用$collection-&gt;load(true) 进行测试并将指定的过滤器视为SQL(我发现这样更容易理解)。

对于 EAV 集合,请使用 addAttributeToFilter()addFieldToFilter(),但第一个可以采用可选的第三个 $joinType 参数。

如何为 EAV 集合上的 ONE 属性添加 OR 过滤器:属性代码是第一个参数,条件是第二个参数。

例如:$col-&gt;addFieldToFilter('name', array(array('like' =&gt; 'M%'), array('like' =&gt; '%O'))); // (get items whose name starts with M OR ends with O)

如何为 EAV 集合上的不同属性添加 OR 过滤器:仅传递一个参数,即一组具有属性的条件。

向 FLAT TABLE 集合添加 OR 条件

在平面表集合上指定具有单个属性的 OR 过滤器与在 EAV 集合上相同。

例如:$col-&gt;addFieldToFilter('name', array(array('like' =&gt; 'M%'), array('like' =&gt; '%O'))); // get items whose name start with M OR end with O

要在 OR 条件中连接多个属性,语法与 EAV 集合不同(注意:这在 Magento 1.6 中已损坏)

例如:$col-&gt;addFieldToFilter(array('weight', 'name'), array(array('in' =&gt; array(1,3)), array('like' =&gt; 'M%')));

第一个属性映射到第一个条件,第二个属性映射到第二个条件,等等

由于这在 1.6 中被破坏,我们可以使用 addFilter() 为多个属性指定简单的相等条件

例如:$col-&gt;addFilter('weight', 1, 'or')-&gt;addFilter('weight', 2, 'or')-&gt;addFilter('sku', 'ABC', 'or'); // weight is 1 or 2 or sku is ABC

您也可以随时使用$collection-&gt;getSelect()-&gt;orWhere(…),但您必须注意跨 RDBMS 的兼容性

注意:这是我多年前在 tumblr 上发布的两件事的交叉帖子:
http://tweetorials.tumblr.com/post/10844018716/eav-collection-or-filters
http://tweetorials.tumblr.com/post/11102525164/flat-table-collection-or-filters

【讨论】:

    【解决方案2】:

    这是我不想使用的解决方案,它通过 Zend_Db 方法修改 SELECT 语句。

    $orderIds = Mage::getModel('sales/order')->getCollection()
        ->getSelect();
    $adapter = $orderIds->getAdapter();
    $quotedFrom = $adapter->quote(
        $fromDate->toString('yyyy-MM-dd HH:mm:ss')
    );
    $quotedTo = $adapter->quote(
        $toDate->toString('yyyy-MM-dd HH:mm:ss')
    );
    $orderIds
        ->where('status = ?', 'processing')
        ->where(
            vsprintf(
                '(created_at >= %s AND created_at <= %s)'
              . ' OR (updated_at >= %s AND updated_at <= %s)',
                array(
                    $quotedFrom, $quotedTo,
                    $quotedFrom, $quotedTo,
                )
            )
        );
    

    【讨论】:

      【解决方案3】:

      也许是时候在核心方面做出一些改变了。我们有一些类似的问题。可能是这样。

      (条件1)或((条件2)和(条件3).....)

      如果您对它的制作方式感兴趣。尝试阅读 http://winzter143.blogspot.com/2012/03/magento-hack-nested-addattributetofilte.html

      【讨论】:

        猜你喜欢
        • 2010-12-12
        • 1970-01-01
        • 1970-01-01
        • 2013-03-27
        • 2014-11-23
        • 2012-03-13
        • 2014-04-18
        • 1970-01-01
        • 2011-06-28
        相关资源
        最近更新 更多