Magento Observer添加命令网格列 - 模糊问题

时间:2013-08-09 13:15:07

标签: magento join magento-1.7

我已经解决了这个问题,最后!

基本上我正在使用$where = $select->getPart('where')获得WHERE部分,然后我会查看每个条件并搜索created_at,如果找到匹配,那么我将created_at替换为main_table.created_at

我已经对此进行了测试,一切正常,如果有什么东西可以“马车”,请告诉我。

谢谢大家!!

public function salesOrderGridCollectionLoadBefore($observer)
{

  $collection = $observer->getOrderGridCollection();
  $select     = $collection->getSelect();
  $select->joinLeft(array('custab' => 'my_custom_table'), 'main_table.entity_id = custab.order_id',array('custab.field_to_show_in_grid'));

  if ($where = $select->getPart('where')) {
      foreach ($where as $key=> $condition) {
          if (strpos($condition, 'created_at')) {
              $new_condition = str_replace("created_at", "main_table.created_at", $condition);
              $where[$key] = $new_condition;
          }
      }
      $select->setPart('where', $where);
  }

}

我正在尝试使用observer从自定义表格中添加销售订单网格中的新列。一切正常,直到我尝试使用列created_at过滤网格。

问题是因为我在自定义表和created_at表中有相同的列名(sales_flat_order_grid)。

我收到此错误

  

SQLSTATE [23000]:完整性约束违规:1052 where子句中的'created_at'列不明确

如果我修改此行'index' => 'created_at'  至  'index' => '**main_table**.created_at'中的app/code/core/Mage/Adminhtml/Block/Sales/Order/Grid.php

$this->addColumn('created_at', array(
        'header' => Mage::helper('sales')->__('Purchased On'),
        'index' => 'main_table.created_at',
        'type' => 'datetime',
        'width' => '100px',
    ));

一切正常,但我不想更改核心文件或将它们复制到本地文件夹并进行编辑,我认为我需要向观察者添加一些简单的解决方案。

这是我的Observer.php

class Testinggrid_ExtendOrderGrid_Model_Observer{
    public function salesOrderGridCollectionLoadBefore($observer)
    {
       $collection = $observer->getOrderGridCollection();
       $select     = $collection->getSelect();
       $select->joinLeft(array('custab' => 'my_custom_table'), 'main_table.entity_id = custab.order_id',array('custab.field_to_show_in_grid'));
    }
}

这是我的模块布局

<layout>
  <sales_order_grid_update_handle>
    <reference name="sales_order.grid">
        <action method="addColumnAfter">
            <columnId>field_to_show_in_grid</columnId>
            <arguments>
                <header>Column header</header>
                <index>field_to_show_in_grid</index>
                <filter_index>field_to_show_in_grid</filter_index>
                <type>text</type>
            </arguments>
            <after>shipping_name</after>
        </action>
    </reference>
  </sales_order_grid_update_handle>
  <adminhtml_sales_order_grid>
    <!-- apply layout handle defined above -->
    <update handle="sales_order_grid_update_handle" />
  </adminhtml_sales_order_grid>
  <adminhtml_sales_order_index>
    <!-- apply layout handle defined above -->
    <update handle="sales_order_grid_update_handle" />
  </adminhtml_sales_order_index>
</layout>

3 个答案:

答案 0 :(得分:4)

经过大量的研究,我已经找到了解决这个问题的各种解决方案,其中没有一个看起来很理想,但有人可能会改进其中一个。

首先,问题的原因。在您加入之前,Magento已经准备了一部分查询,表示为对象。这部分设置了'select'和“where”,需要进行过滤,这将导致查询看起来像:

SELECT `main_table`.* FROM `sales_flat_order_grid`
WHERE (`grand_total` <= '20')

问题是查询的“where”部分,因为列名不以表名别名main_table作为前缀。如果您尝试通过向表中添加连接来扩展此查询,该表具有与where子句中的任何列同名的列,则会出现“模糊”错误。例如,如果您要将上述查询加入sales_flat_order表,那么它将失败,因为该表也有grand_total列,使得过滤器中的'where'子句不明确。

Magento有一个处理这个问题的机制,filterIndex。构成网格的列对象可以使用

设置过滤器索引
$column = $block->getColumn('grand_total');
$column->setFilterIndex('main_table.grand_total');

将上述查询中的“where”子句调整为WHERE `main_table`.`grand_total` <= '20'

如果我们能够在SQL实际准备好之前到达$column对象,那将解决所有问题。

解决方案

  1. 覆盖核心文件
  2. 覆盖和子类Mage_Adminhtml_Block_Widget_Grid类。只覆盖_prepareColumns()方法,如下所示:

    class Mycomp_Mymodule_Block_Sales_Order_Grid extends Mage_Adminhtml_Block_Sales_Order_Grid
    {
        protected function _prepareColumns()
        {
            //get the columns from the parent so they can be returned as expected
            $columns = parent::_prepareColumns();
            $this->addColumnAfter('custom_column', array(
                'header'    => 'Custom Column',
                'index'     => 'custom_column',
                'type'      => 'text',
                'filter_index'=> 'custom_table.custom_column',
            ), 'billing_name' );
            //columns need to be reordered
            $this->sortColumnsByOrder();
            return $columns;
        }
    }
    
    1. 在执行
    2. 之前重写查询

      这是问题中显示的解决方案,您可以在其中获取'where'子句,然后执行某种查找和替换以添加main_table前缀。像

      这样的东西
      $select = $collection->getSelect();    
      $where = $select->getPart('where');
      //find an replace each element in the where array
      
      1. 在“加入”子句中使用子查询
      2. 由于问题是由两个表中具有相同名称的列引起的,因此可以更改“join”查询以重命名或删除不明确的列。将join子句调整为:

        $subquery = new Zend_Db_Expr( 
            '(SELECT order_id AS order_alias, 
                     field_to_show_in_grid AS field_to_show_in_grid_alias
              FROM my_custom_table)');
        $select->joinLeft( array('custab' => $subquery), 
                           'main_table.entity_id = custab.order_alias',
                           array('custab.field_to_show_in_grid_alias'));
        

        虽然这确实有效,但查询太慢而不实用。 可以修改查询以加快速度:

        $subquery = new Zend_Db_Expr( 
            '(SELECT field_to_show_in_grid
              FROM my_custom_table
              WHERE main_table.entity_id=my_custom_table.order_id)');
        $select->addFieldToSelect( array('field_to_show_in_grid_alias'=>$subquery) );
        

        虽然这很有效但速度很快,但问题在于Magento使用构成此查询的对象两次,一次用于网格本身,也用于“计数”以在网格页面顶部生成分页wigits。不幸的是,当在'count'查询中使用时,Magento不使用所选列,只使用查询的'from','join'和'where'部分。这意味着如果您使用上述查询,则无法过滤要添加的新列。

        1. 将列添加到sales_flat_order_grid
        2. sales_flat_order_grid表可以扩展为包含您需要的额外列,并自动更新该列。这里描述了这种技术https://magento.stackexchange.com/a/4626/34327

          1. 在构建查询之前拦截并添加过滤器索引
          2. 在SQL完全准备好之前,有一个事件可以被截获,查询和更改。这是resource_get_tablename,但您需要检查是否正在使用正确的表格。

            为了实现这一目标,我将编写resource_get_tablename事件的观察者,如下所示

            public function resourceGetTablename(Varien_Event_Observer $observer)
            {
                //Check we are working with the correct table
                if( $observer->getTableName() == 'sales_flat_order_grid' ) {
            
                    $block = Mage::getSingleton('core/layout')->getBlock('sales_order.grid');
            
                    foreach( $block->getColumns() as $column ) {
                        //create a filter index for each column using 'main_table' prefixed to the column index
                        $column->setFilterIndex('main_table.' . $column->index);
                    }
            
                }
            }
            

            我希望有人可以改进其中一种方法来创造真正有用的东西。

答案 1 :(得分:2)

尝试更改

<filter_index>field_to_show_in_grid</filter_index>

<filter_index>main_table.created_at</filter_index>

请参阅Adding a column to Magento orders grid - alternative way using layout handles

答案 2 :(得分:0)

您可以在观察者中使用$collection->addFilterToMap('created_at', 'main_table.created_at');