addFilter vs addFieldToFilter

19

A coleção Magento tem dois métodos para filtrar:

1. Varien_Data_Collection_Db::addFieldToFilter
2. Varien_Data_Collection::addFilter

Parece que os dois métodos adicionam onde a condição Zend_Db_Select. E que vantagens addFiltertraz? Quando devo usá-lo em vez de addFieldToFilter?

Lindar
fonte

Respostas:

49

OK, vamos examiná-los. A primeira diferença é que addFilter()é mais genérico e não específico do banco de dados. Também é usado Varien_Directory_Collectionpara filtrar por nome de arquivo. Mas para esta resposta eu vou focar Varien_Data_Collection_Db.

Eles têm uma assinatura diferente, onde addFilterparece ser menos flexível, mas você verá que também tem suas vantagens:

1. addFieldToFilter ()

/**
 * Add field filter to collection
 *
 * @see self::_getConditionSql for $condition
 *
 * @param   string|array $field
 * @param   null|string|array $condition
 *
 * @return  Mage_Eav_Model_Entity_Collection_Abstract
 */
public function addFieldToFilter($field, $condition = null)

Parâmetros

addFieldToFilter () pode usar uma matriz de campos com uma matriz de condições ou um único campo com uma única condição:

  • addFieldToFilter('field', 'value')

    Resulta em: field=value

  • addFieldToFilter(['field1', 'field2'], ['value1', 'value2']);

    Resulta em: field1=value1 OR field2=value2

Cada condição pode ser:

  • um único valor escalar (como 'value1'e 'value2'acima)
  • uma matriz no formulário [ operator => value ]
  • um Zend_Db_Exprobjeto
  • uma matriz de condições combinadas com "OR" (sim, isso é recursivo)

Isso, especialmente a sintaxe "operator => value", está documentada no código em Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- lembre-se disso, eu as busco com frequência:

 * If $condition integer or string - exact value will be filtered ('eq' condition)
 *
 * If $condition is array - one of the following structures is expected:
 * - array("from" => $fromValue, "to" => $toValue)
 * - array("eq" => $equalValue)
 * - array("neq" => $notEqualValue)
 * - array("like" => $likeValue)
 * - array("in" => array($inValues))
 * - array("nin" => array($notInValues))
 * - array("notnull" => $valueIsNotNull)
 * - array("null" => $valueIsNull)
 * - array("moreq" => $moreOrEqualValue)
 * - array("gt" => $greaterValue)
 * - array("lt" => $lessValue)
 * - array("gteq" => $greaterOrEqualValue)
 * - array("lteq" => $lessOrEqualValue)
 * - array("finset" => $valueInSet)
 * - array("regexp" => $regularExpression)
 * - array("seq" => $stringValue)
 * - array("sneq" => $stringValue)
 *
 * If non matched - sequential array is expected and OR conditions
 * will be built using above mentioned structure

Há um recurso não documentado adicional no operador from/ to:

  • com ['from' => $dateFrom, 'to' => $dateTo, 'date' => true]os valores $dateFrome $dateToserão analisados ​​como datas. Eles podem ter qualquer forma que seja aceita porVarien_Date::formatDate()
  • se você precisar do recurso de análise de data, mas apenas para comparar um de <=ou >=, poderá omitir um 'from'ou 'to'.
  • 'datetime' => truetambém deve funcionar e incluir a hora, não apenas o dia, mas há um erro no Varien_Db_Adapter_Pdo_Mysql :: _ prepareSqlDateCondition () ( $includeTimestampparâmetro ausente ) que faz o datetimetrabalho da mesma maneira que date. Ambos incluem o tempo. Portanto, se você precisar comparar apenas por data, adicione 00:00:00à fromdata e 23:59:59à todata.

Mapeamento de campo

O método usa mapeamento de campo. Os mapeamentos de campo podem ser definidos em classes de coleta concretas para criar nomes de campos alternativos. Aqui está um exemplo da coleção de produtos:

protected $_map = array('fields' => array(
    'price'         => 'price_index.price',
    'final_price'   => 'price_index.final_price',
    'min_price'     => 'price_index.min_price',
    'max_price'     => 'price_index.max_price',
    'tier_price'    => 'price_index.tier_price',
    'special_price' => 'price_index.special_price',
));

2. addFilter ()

/**
 * Add collection filter
 *s
 * @param string $field
 * @param string $value
 * @param string $type and|or|string
 */
public function addFilter($field, $value, $type = 'and')

Parâmetros

addFilter()permite apenas filtrar um único campo por um único valor e um tipo . $typepode ser qualquer um de:

  • "e" (padrão) - adiciona AND $field=$valueà cláusula WHERE (é claro, com citações apropriadas)
  • "or" - adiciona "OR $field=$valueà cláusula WHERE (idem)
  • "string" - adiciona AND $valueà cláusula WHERE (ou seja, $ value pode ser uma expressão SQL arbitrária)
  • "público" - usa mapeamento de campo e _getConditionSql(), semelhante a addFieldToFilter(). Isso o torna quase tão poderoso quanto, apenas falta o recurso de adicionar vários filtros para campos diferentes combinados com OU.

Em Varien_Data_Collection_Db::_renderFilters()você pode ver como eles são processados.

Extensibilidade

Há uma diferença importante que é uma vantagem addFilter(). Ele coleta os filtros a serem aplicados $this->_filters()e os adiciona apenas ao Zend_Db_Selectobjeto de consulta antes de carregar a coleção. addFieldToFilter()por outro lado, manipula o objeto de consulta imediatamente.

Isso permite manipular ou remover filtros que já foram adicionados. A coleção Varien não possui uma interface, você deve implementá-la em sua coleção personalizada. Existe um método de gancho _renderFiltersBefore()que você pode substituir.

Fabian Schmengler
fonte
Eu tenho uma pergunta que podemos usar addFiltercom attributes?
Murtuza Zabuawala
@MurtuzaZabuawala não, não pode ser usado para atributos de EAV #
Fabian Schmengler
Obrigado por esta resposta Fabian, eu gostei da publicação do seu site sobre isso também, mas Qual é o valor de $ field no addFilter? Estou tentando usar a função addFilter para filtrar apenas os produtos que estão na categoria do módulo está sendo executado em
John
AFAIK não é possível, pois as categorias não são atributos, mas associadas a produtos em uma tabela separada. Não pode dar-lhe uma solução em cima da minha cabeça, desculpe
Fabian Schmengler
Obrigado por responder, não se preocupe, se eu encontrar uma maneira de contornar isso, eu atualizarei aqui com a minha solução #
John
2

A coleção Magento tem dois métodos para filtrar abaixo diferentes

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ field, $ condition = null)

O primeiro parâmetro de addFieldToFilteré o atributo pelo qual você deseja filtrar. O segundo é o valor que você está procurando. Aqui estamos adicionando um skufiltro para o valor n2610.

O segundo parâmetro também pode ser usado para especificar o tipo de filtragem que você deseja fazer. É aqui que as coisas ficam um pouco complicadas e vale a pena aprofundar um pouco mais.

Então, por padrão, o seguinte

$collection_of_products->addFieldToFilter('sku','n2610'); 

é (essencialmente) equivalente a

WHERE sku = "n2610"

Dê uma olhada por si mesmo. Executando o seguinte

public function testAction()
{
    var_dump(
    (string) 
    Mage::getModel('catalog/product')
    ->getCollection()
    ->addFieldToFilter('sku','n2610')
    ->getSelect());
}

vai render

SELECT `e`.* FROM `catalog_product_entity` AS `e` WHERE (e.sku = 'n2610')'

Lembre-se de que isso pode ficar complicado rapidamente se você estiver usando um atributo EAV. Adicionar um atributo

var_dump(
(string) 
Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('meta_title','my title')
->getSelect()
);

e a consulta fica complicada.

SELECT `e`.*, IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) AS `meta_title` 
FROM `catalog_product_entity` AS `e` 
INNER JOIN `catalog_product_entity_varchar` AS `_table_meta_title_default` 
    ON (_table_meta_title_default.entity_id = e.entity_id) AND (_table_meta_title_default.attribute_id='103') 
    AND _table_meta_title_default.store_id=0        
LEFT JOIN `catalog_product_entity_varchar` AS `_table_meta_title` 
    ON (_table_meta_title.entity_id = e.entity_id) AND (_table_meta_title.attribute_id='103') 
    AND (_table_meta_title.store_id='1') 
WHERE (IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) = 'my title')

Para não insistir no assunto, tente não pensar muito no SQL se estiver dentro do prazo.

Outros operadores de comparação Tenho certeza de que você está se perguntando "e se eu quiser algo diferente de igual por consulta"? Diferente, maior que, menor que etc. O segundo parâmetro do método addFieldToFilter também o abordou. Ele suporta uma sintaxe alternativa, onde, em vez de passar uma string, você passa um único elemento Array.

A chave dessa matriz é o tipo de comparação que você deseja fazer. O valor associado a essa chave é o valor pelo qual você deseja filtrar. Vamos refazer o filtro acima, mas com esta sintaxe explícita

public function testAction()
{
    var_dump(
    (string) 
    Mage::getModel('catalog/product')
    ->getCollection()
    ->addFieldToFilter('sku',array('eq'=>'n2610'))
    ->getSelect()
    );          
}

Chamando nosso filtro

addFieldToFilter('sku',array('eq'=>'n2610'))

Como você pode ver, o segundo parâmetro é uma matriz PHP. Sua chave é eq, que significa igual. O valor dessa chave é n2610, que é o valor que estamos filtrando.

O Magento possui vários filtros semelhantes ao idioma inglês que trarão uma lágrima de lembrança (e talvez dor) a qualquer desenvolvedor de perl antigo na platéia.

Listados abaixo estão todos os filtros, juntamente com um exemplo de seus equivalentes SQL.

array("eq"=>'n2610')
WHERE (e.sku = 'n2610')

array("neq"=>'n2610')
WHERE (e.sku != 'n2610')

array("like"=>'n2610')
WHERE (e.sku like 'n2610')

array("nlike"=>'n2610')
WHERE (e.sku not like 'n2610')

array("is"=>'n2610')
WHERE (e.sku is 'n2610')

array("in"=>array('n2610'))
WHERE (e.sku in ('n2610'))

array("nin"=>array('n2610'))
WHERE (e.sku not in ('n2610'))

array("notnull"=>'n2610')
WHERE (e.sku is NOT NULL)

array("null"=>'n2610')
WHERE (e.sku is NULL)

array("gt"=>'n2610')
WHERE (e.sku > 'n2610')

array("lt"=>'n2610')
WHERE (e.sku < 'n2610')

array("gteq"=>'n2610')
WHERE (e.sku >= 'n2610')

array("moreq"=>'n2610') //a weird, second way to do greater than equal
WHERE (e.sku >= 'n2610')

array("lteq"=>'n2610')
WHERE (e.sku <= 'n2610')

array("finset"=>array('n2610'))
WHERE (find_in_set('n2610',e.sku))

array('from'=>'10','to'=>'20')
WHERE e.sku >= '10' and e.sku <= '20'

A maioria delas é auto-explicativa, mas algumas merecem destaque especial

in, nin, find_in_set Os condicionais in e nin permitem passar uma matriz de valores. Ou seja, é permitido que a parte do valor da sua matriz de filtros seja uma matriz.

array("in"=>array('n2610','ABC123')
WHERE (e.sku in ('n2610','ABC123'))

notnull, null A palavra-chave NULL é especial na maioria dos tipos de SQL. Normalmente, não funciona bem com o operador de igualdade padrão (=). A especificação de nulo ou nulo como seu tipo de filtro fornecerá a sintaxe correta para uma comparação NULL, ignorando o valor que você passar

array("notnull"=>'n2610')
WHERE (e.sku is NOT NULL)

de - para filtrar Esse é outro formato especial que quebra a regra padrão. Em vez de uma matriz de elemento único, você especifica uma matriz de dois elementos. Um elemento tem a chave de, o outro elemento tem a chave de. Conforme as teclas indicadas, esse filtro permite criar um intervalo de / para sem ter que se preocupar com símbolos maiores e menores que

public function testAction
{
        var_dump(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('price',array('from'=>'10','to'=>'20'))
        ->getSelect()
        );                      
}

Os rendimentos acima

WHERE (_table_price.value >= '10' and _table_price.value <= '20')'

AND ou OR, ou são OR e AND? Finalmente, chegamos aos operadores booleanos. É o raro momento em que estamos filtrando apenas um atributo. Felizmente, as coleções do Magento nos cobrem. Você pode encadear várias chamadas para addFieldToFilter para obter um número de consultas "AND".

function testAction()
{
        echo(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('sku',array('like'=>'a%'))
        ->addFieldToFilter('sku',array('like'=>'b%'))
        ->getSelect()
        );                                  
}

Ao encadear várias chamadas como acima, produziremos uma cláusula where que se parece com a seguinte

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

Para aqueles que levantaram sua mão, sim, o exemplo acima sempre retornaria 0 registros. Nenhum sku pode começar com AMB e a e b. O que provavelmente queremos aqui é uma consulta OR. Isso nos leva a outro aspecto confuso do segundo parâmetro do addFieldToFilter.

Se você deseja criar uma consulta OR, precisa passar uma Matriz de filtro Matrizes como o segundo parâmetro. Acho melhor atribuir seu filtro individual Arrays a variáveis

public function testAction()
{
        $filter_a = array('like'=>'a%');
        $filter_b = array('like'=>'b%');
}

e depois atribuir uma matriz de todas as minhas variáveis ​​de filtro

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

No interesse de ser explícito, aqui está a matriz acima mencionada de matrizes de filtro.

array($filter_a,$filter_b)

Isso nos dará uma cláusula WHERE que se parece com a seguinte

WHERE (((e.sku like 'a%') or (e.sku like 'b%')))
  1. Varien_Data_Collection :: addFilter
 addFilter($field, $value, $type = 'and')

addFilter()permite apenas filtrar um único campo por um único valor e um tipo. $typepode ser qualquer um de:

  1. "and" (padrão) - adiciona AND $ field = $ value à cláusula WHERE
  2. "or" - adiciona "OR $ field = $ value à cláusula WHERE

Veja mais detalhes

Abdul
fonte
11
Isso não explica nada.
Fabian Schmengler
Isso não faz sentido. Ele não descreve diferença desses métodos
Lindar
2
Sua resposta atualizada é copiada principalmente de alanstorm.com/magento_collections . Por favor, cite suas fontes pelo menos!
Fabian Schmengler