addFilter vs addFieldToFilter

19

La colección Magento tiene dos métodos para filtrar:

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

Parece que ambos métodos agregan donde condición Zend_Db_Select. ¿Y qué ventajas addFiltertrae? ¿Cuándo debería usarlo en lugar de addFieldToFilter?

Lindar
fuente

Respuestas:

49

OK, examinémoslos. La primera diferencia es que addFilter()es más genérica y no específica de la base de datos. También se usa Varien_Directory_Collectionpara filtrar por nombre de archivo. Pero para esta respuesta me voy a centrar Varien_Data_Collection_Db.

Sus métodos tienen una firma diferente, donde addFilterparece ser menos flexible, pero verás que también tiene sus ventajas:

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 () puede tomar una matriz de campos con una variedad de condiciones, o un solo campo con una sola condición:

  • addFieldToFilter('field', 'value')

    Resultados en: field=value

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

    Resultados en: field1=value1 OR field2=value2

Cada condición puede ser:

  • un solo valor escalar (como 'value1'y 'value2'arriba)
  • una matriz en forma [ operator => value ]
  • un Zend_Db_Exprobjeto
  • una serie de condiciones que se combinan con "OR" (sí, eso es recursivo)

Esto, especialmente la sintaxis "operator => value" está documentada en el código en Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- recuerde esto, los busco con bastante frecuencia:

 * 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

Hay una función adicional no documentada en el operador from/ to:

  • con ['from' => $dateFrom, 'to' => $dateTo, 'date' => true]los valores $dateFromy $dateTose analizarán como fechas. Pueden estar en cualquier forma aceptada porVarien_Date::formatDate()
  • si necesita la función de análisis de fechas pero solo para comparar uno de <=o >=, puede omitir uno 'from'o 'to'.
  • 'datetime' => truese supone que funciona también e incluye la hora, no solo el día, sino que hay un error en Varien_Db_Adapter_Pdo_Mysql :: _ prepareSqlDateCondition () ( $includeTimestampparámetro faltante ) que hace que el datetimetrabajo sea igual que date. Ambos incluyen el tiempo. Entonces, si necesita comparar solo por fecha, agregue 00:00:00a la fromfecha y 23:59:59a la tofecha.

Mapeo de campo

El método usa mapeo de campo. Las asignaciones de campo se pueden definir en clases de colección concretas para crear nombres de campo de alias. Aquí hay un ejemplo de la colección de productos:

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()solo permite filtrar un solo campo por un solo valor y un tipo . $typepuede ser cualquiera de:

  • "y" (predeterminado): se agrega AND $field=$valuea la cláusula WHERE (por supuesto, con una cita adecuada)
  • "o" - se agrega "OR $field=$valuea la cláusula WHERE (ídem)
  • "cadena": se agrega AND $valuea la cláusula WHERE (es decir, $ value puede ser una expresión SQL arbitraria)
  • "público" - la asignación de campos y usos _getConditionSql(), similar a addFieldToFilter(). Esto lo hace casi tan poderoso, solo le falta la característica de agregar múltiples filtros para diferentes campos combinados con OR.

En Varien_Data_Collection_Db::_renderFilters()puedes ver cómo se procesan.

Extensibilidad

Hay una diferencia importante para la cual es una ventaja addFilter(). Recopila los filtros que se aplicarán $this->_filters()y solo los agrega al Zend_Db_Selectobjeto de consulta justo antes de cargar la colección. addFieldToFilter()por otro lado manipula el objeto de consulta de inmediato.

Esto le permite manipular o eliminar filtros que ya se han agregado. La colección Varien no tiene una interfaz para ello, debe implementar esto en su colección personalizada. Hay un método de enlace _renderFiltersBefore()que puede anular.

Fabian Schmengler
fuente
Tengo una sola pregunta ¿podemos usar addFiltercon attributes?
Murtuza Zabuawala
@MurtuzaZabuawala no, no se puede usar para atributos EAV
Fabian Schmengler
Gracias por esta respuesta Fabian, también disfruté de la publicación de su sitio web sobre esto, pero ¿Qué valor puede contener $ field en addFilter? Estoy tratando de usar la función addFilter para filtrar solo los productos que están en la categoría en la que se está ejecutando el módulo
John
AFAIK no es posible ya que las categorías no son atributos sino que están asociadas a productos en una tabla separada. No puedo darle una solución encima de mi cabeza, lo siento
Fabian Schmengler
Gracias por responder, no se preocupe, si encuentro una solución, actualizaré aquí con mi solución
John
2

La colección Magento tiene dos métodos para filtrar abajo diferentes

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ campo, $ condición = nulo)

El primer parámetro de addFieldToFilteres el atributo por el que desea filtrar. El segundo es el valor que estás buscando. Aquí estamos agregando un skufiltro para el valor n2610.

El segundo parámetro también se puede usar para especificar el tipo de filtrado que desea hacer. Aquí es donde las cosas se ponen un poco complicadas, y vale la pena profundizar un poco más.

Entonces, por defecto, lo siguiente

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

es (esencialmente) equivalente a

WHERE sku = "n2610"

Echa un vistazo por ti mismo. Ejecutando lo siguiente

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

rendirá

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

Tenga en cuenta que esto puede complicarse rápidamente si está utilizando un atributo EAV. Agregar un atributo

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

y la consulta se vuelve retorcida.

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

No para diferir el punto, pero trata de no pensar demasiado en el SQL si estás en la fecha límite.

Otros operadores de comparación Estoy seguro de que se está preguntando "¿y si quiero algo más que un igual por consulta"? No es igual, mayor que, menor que, etc. El segundo parámetro del método addFieldToFilter también lo cubre allí. Admite una sintaxis alternativa donde, en lugar de pasar una cadena, pasa una matriz de un solo elemento.

La clave de esta matriz es el tipo de comparación que desea hacer. El valor asociado con esa clave es el valor por el que desea filtrar. Vamos a rehacer el filtro anterior, pero con esta sintaxis explícita

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

Llamando a nuestro filtro

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

Como puede ver, el segundo parámetro es una matriz de PHP. Su clave es eq, que significa igual. El valor de esta clave es n2610, que es el valor en el que estamos filtrando.

Magento tiene varios de estos filtros en inglés que traerán una lágrima de recuerdo (y tal vez dolorosa) a cualquier antiguo desarrollador de Perl en la audiencia.

A continuación se enumeran todos los filtros, junto con un ejemplo de sus 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'

La mayoría de estos se explican por sí mismos, pero algunos merecen un llamado especial

in, nin, find_in_set Los condicionales in y nin le permiten pasar una matriz de valores. Es decir, la porción de valor de su matriz de filtros puede ser una matriz.

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

notnull, null La palabra clave NULL es especial en la mayoría de los tipos de SQL. Por lo general, no jugará bien con el operador de igualdad estándar (=). Si especifica notnull o null como su tipo de filtro, obtendrá la sintaxis correcta para una comparación NULL e ignorará cualquier valor que pase

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

from - to filter Este es otro formato especial que rompe la regla estándar. En lugar de una matriz de un solo elemento, especifique una matriz de dos elementos. Un elemento tiene la clave de, el otro elemento tiene la clave para. Como indican las teclas, este filtro le permite construir un rango desde / hasta sin tener que preocuparse por símbolos mayores o menores que

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

Los rendimientos anteriores

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

AND u OR, o es eso OR y AND? Finalmente, llegamos a los operadores booleanos. Es el raro momento en el que solo estamos filtrando por un atributo. Afortunadamente, las colecciones de Magento nos tienen cubiertos. Puede encadenar varias llamadas a addFieldToFilter para obtener una serie de consultas "Y".

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

Al encadenar varias llamadas como arriba, produciremos una cláusula where que se parece a la siguiente

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

Para aquellos de ustedes que acaban de levantar la mano, sí, el ejemplo anterior siempre devolverá 0 registros. Ningún sku puede comenzar con AMBOS a a y a b. Lo que probablemente queremos aquí es una consulta OR. Esto nos lleva a otro aspecto confuso del segundo parámetro de addFieldToFilter.

Si desea crear una consulta OR, debe pasar una matriz de matrices de filtro como segundo parámetro. Creo que es mejor asignar sus matrices de filtro individuales a variables

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

y luego asignar una matriz de todas mis variables 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()
        );
}

En aras de ser explícito, aquí está la matriz de matrices de filtros mencionada anteriormente.

array($filter_a,$filter_b)

Esto nos dará una cláusula WHERE que se parece a la siguiente

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

addFilter()solo permite filtrar un solo campo por un solo valor y un tipo. $typepuede ser cualquiera de:

  1. "y" (predeterminado): agrega AND $ field = $ value a la cláusula WHERE
  2. "o" - agrega "OR $ field = $ value a la cláusula WHERE

Ver más detalles

Abdul
fuente
1
Esto no explica nada.
Fabian Schmengler
Esto no tiene ningún sentido. No describe la diferencia de estos métodos
Lindar
2
Su respuesta actualizada se copia principalmente de alanstorm.com/magento_collections . ¡Por favor cita tus fuentes al menos!
Fabian Schmengler