Conflitos na cláusula where com nomes de coluna ambíguos

28

Um pouco de contexto para isso. Quero estender a função de exportação de pedidos de vendas (via grade) para ter mais colunas. Eu criei um módulo que adiciona uma nova grade para exportação e também um novo modelo de coleção que estende o original. Isso usa a função _beforeLoad () para que eu possa ingressar nas tabelas necessárias.

O problema que estou tendo é que, quando os filtros da grade são adicionados (o increment_id, a data do pedido etc.), a cláusula where que isso adiciona não prefixa a tabela e estou tendo problemas com nomes de colunas ambíguos. Por exemplo, em increment_id, estou tendo o problema na cláusula where:

SELECT `main_table`.*, `sales`.`total_qty_ordered`, `sales`.`entity_id` AS `order_id`, `sagepay`.`vendor_tx_code` FROM `sales_flat_order_grid` AS `main_table`
 LEFT JOIN `sales_flat_order` AS `sales` ON main_table.increment_id = sales.increment_id
 LEFT JOIN `sagepaysuite_transaction` AS `sagepay` ON order_id = sagepay.order_id WHERE (increment_id LIKE '%100000261%') GROUP BY `main_table`.`entity_id`

Esta cláusula where é adicionada antes que eu faça as junções às outras tabelas na função _addColumnFilterToCollection ()

protected function _addColumnFilterToCollection($column)
    {
        if ($this->getCollection()) {
            $field = ( $column->getFilterIndex() ) ? $column->getFilterIndex() : $column->getIndex();
            if ($column->getFilterConditionCallback()) {
                call_user_func($column->getFilterConditionCallback(), $this->getCollection(), $column);
            } else {
                $cond = $column->getFilter()->getCondition();
                if ($field && isset($cond)) {
                    // Filter added at this point
                    $this->getCollection()->addFieldToFilter($field , $cond);
                }
            }
        }
        return $this;
    }

Como um breve teste, mudei a linha para

$this->getCollection()->addFieldToFilter('main_table.' . $field , $cond);

e isso funcionou, mas não é uma ótima maneira de fazê-lo.

Meu código em _beforeLoad () é

protected function _beforeLoad()
{
    // Join the sales_flat_order table to get order_id and and total_qty_ordered
    $this->getSelect()->joinLeft(array('sales' => $this->getTable('sales/order')),
        'main_table.increment_id = sales.increment_id',
        array('total_qty_ordered' => 'sales.total_qty_ordered',
              'order_id' => 'sales.entity_id'));

    // Join the SagePay transaction table to get vendor_tx_code
    $this->getSelect()->joinLeft(array('sagepay' => $this->getTable('sagepaysuite2/sagepaysuite_transaction')),
        'order_id = sagepay.order_id',
        array('vendor_tx_code' => 'vendor_tx_code'));

    $this->getSelect()->group('main_table.entity_id');
    parent::_beforeLoad();
}

Preciso usar o increment_id para ingressar na tabela de grade de pedidos de vendas e na tabela de transações SagePay, pois esse é o único ID comum que posso ver.

Basicamente, estou me perguntando qual é a melhor abordagem para lidar com isso. Provavelmente eu poderia fazer a alteração mencionada acima, mas isso não parece certo. Existe algo que eu possa mudar nas minhas declarações de associação?

Obrigado.

Paulo
fonte
1
Como você se juntou às mesas? Trabalhar no modelo Zend_Db_Select é uma má idéia, porque o magento registra as tabelas conjuntas e adiciona normalmente todos os prefixos. Eu escrevi um artigo no blog sobre como ingressar, talvez ele ajuda: blog.fabian-blechschmidt.de/articles/...
Fabian Blechschmidt
Obrigado pela resposta, vou ler isso. Tentei usar o joinTable (), mas ele não estava disponível no modelo de coleção.
Paul

Respostas:

52

Você pode resolver facilmente qualquer condição where ambígua usando o seguinte método de coleta:

  • addFilterToMap($filterName, $alias, $group = 'fields')
    • $filter- é o nome do filtro usado no addFieldToFilter()método, para o seu caso éincrement_id
    • $alias- é o nome completo da coluna que está associada ao filtro, para o seu caso main_table.increment_id.
    • $group - foi planejado para ser um mapa para qualquer tipo de informação na coleção, mas por enquanto é usado apenas em filtros, para que você possa omitir esse argumento.

Também não acho que beforeLoad seja o lugar certo para você se unir, a menos que esteja observando um evento. No seu caso, é melhor movê-lo para o _initSelect()método com a chamada anterior parent::_initSelect(). Você pode chamar o addFilterToMap()método dentro do seu _initSelect()método para resolver conflitos de junção, assim:

$this->addFilterToMap('increment_id', 'main_table.increment_id');
Ivan Chepurnyi
fonte
Por interesse, por que é melhor fazer as junções em _initSelect ()?
Paul
@Paul _initSelecté executado apenas uma vez o tempo todo, _beforeLoadpode ser chamado mais de uma vez, pois você pode load()coletar mais de uma vez se redefinir seu estado.
Ivan Chepurnyi
@Paul também, como _beforeLoad pode ser chamado duas vezes, você receberá um erro fatal do Zend_Db_Select na segunda chamada.
Ivan Chepurnyi
2
Eu fiz algo assim: $collection = Mage::getModel("education/ticket") ->getCollection() ->addFilterToMap('updated_at', 'main_table.updated_at') ->addFilterToMap('created_at', 'main_table.created_at');
FosAvance 14/11
@IvanChepurnyi, são geniais. Isso reflete que você é excelente como arquiteto do Magento 1
Amit Bera