addFilter vs addFieldToFilter


19

La raccolta Magento ha due metodi per filtrare:

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

Sembra che entrambi i metodi aggiungano la condizione a Zend_Db_Select. E quali vantaggi addFilterporta? Quando dovrei usarlo anziché addFieldToFilter?

Risposte:


49

OK, esaminiamoli. La prima differenza è che addFilter()è più generico e non specifico del database. Viene anche utilizzato da Varien_Directory_Collectionper filtrare in base al nome del file. Ma per questa risposta mi concentrerò su Varien_Data_Collection_Db.

I metodi hanno una firma diversa, dove addFiltersembra essere meno flessibile, ma vedrai che ha anche i suoi vantaggi:

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)

parametri

addFieldToFilter () può accettare una matrice di campi con una matrice di condizioni o un singolo campo con una singola condizione:

  • addFieldToFilter('field', 'value')

    Risultati in: field=value

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

    Risultati in: field1=value1 OR field2=value2

Ogni condizione può essere:

  • un singolo valore scalare (come 'value1'e 'value2'sopra)
  • un array nel modulo [ operator => value ]
  • un Zend_Db_Exproggetto
  • una serie di condizioni che sono combinate con "OR" (sì, è ricorsivo)

Questa, in particolare la sintassi "operator => value" è documentata nel codice su Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- ricordate questo, li guardo abbastanza spesso:

 * 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

C'è un'altra funzione non documentata nell'operatore from/ to:

  • con ['from' => $dateFrom, 'to' => $dateTo, 'date' => true]i valori $dateFrome $dateToverranno analizzati come date. Possono essere in qualsiasi forma accettata daVarien_Date::formatDate()
  • se hai bisogno della funzione di analisi della data ma solo per confrontare una delle <=o >=, puoi omettere 'from'o 'to'.
  • 'datetime' => truedovrebbe funzionare anche e includere l'ora, non solo il giorno, ma c'è un bug in Varien_Db_Adapter_Pdo_Mysql :: _ preparSqlDateCondition () ( $includeTimestampparametro mancante ) che fa datetimefunzionare allo stesso modo di date. Entrambi includono il tempo. Quindi, se devi confrontare solo per data, aggiungi 00:00:00alla fromdata e 23:59:59alla todata.

Mappatura dei campi

Il metodo utilizza la mappatura dei campi. I mapping dei campi possono essere definiti in classi di raccolta concrete per creare nomi di campi alias. Ecco un esempio dalla collezione di prodotti:

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

parametri

addFilter() consente di filtrare un singolo campo solo per un singolo valore e a tipo . $typepuò essere uno dei seguenti:

  • "and" (impostazione predefinita) - aggiunge AND $field=$valuealla clausola WHERE (ovviamente con la citazione corretta)
  • "or" - aggiunge "OR $field=$valuealla clausola WHERE (idem)
  • "stringa" - aggiunge AND $valuealla clausola WHERE (ovvero $ value può essere un'espressione SQL arbitraria)
  • "pubblico": utilizza la mappatura dei campi e _getConditionSql(), in modo simile aaddFieldToFilter() . Questo lo rende quasi altrettanto potente, manca solo la funzione di aggiungere più filtri per diversi campi combinati con OR.

Nel Varien_Data_Collection_Db::_renderFilters() puoi vedere come vengono elaborati.

Estensibilità

C'è una differenza importante per cui è un vantaggio addFilter(). Raccoglie i filtri da applicare $this->_filters()e li aggiunge solo Zend_Db_Selectall'oggetto query subito prima di caricare la raccolta.addFieldToFilter()d'altra parte manipola immediatamente l'oggetto query.

Ciò consente di manipolare o rimuovere i filtri che sono già stati aggiunti. La collezione Varien non ha un'interfaccia per essa, devi implementarla nella tua collezione personalizzata. C'è un metodo hook _renderFiltersBefore()che puoi ignorare.


Ho una sola domanda possiamo usare addFiltercon attributes?
Murtuza Zabuawala,

@MurtuzaZabuawala no, non può essere utilizzato per gli attributi EAV
Fabian Schmengler

Grazie per questa risposta Fabian, mi è piaciuto anche il post del tuo sito web su questo, ma quale valore può contenere $ field in addFilter? sto cercando di utilizzare la funzione addFilter per filtrare solo i prodotti che rientrano nella categoria in cui è in esecuzione il modulo
John,

AFAIK non è possibile poiché le categorie non sono attributi ma associati a prodotti in una tabella separata. Non si può dare una soluzione in cima alla mia testa, mi dispiace
Fabian SCHMENGLER

Grazie per la risposta, non preoccuparti, se trovo un modo per farlo, aggiornerò qui con la mia soluzione
John,

2

La collezione Magento ha due metodi per filtrare qui sotto diversi

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ field, $ condition = null)

Il primo parametro di addFieldToFilterè l'attributo per cui si desidera filtrare. Il secondo è il valore che stai cercando. Qui stiamo aggiungendo un skufiltro per il valore n2610.

Il secondo parametro può essere utilizzato anche per specificare il tipo di filtro che si desidera eseguire. È qui che le cose si complicano e vale la pena approfondire un po 'di più.

Quindi, per impostazione predefinita, il seguente

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

è (essenzialmente) equivalente a

WHERE sku = "n2610"

Dai un'occhiata a te stesso. In esecuzione il seguente

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

cederà

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

Tieni presente che ciò può complicarsi rapidamente se stai utilizzando un attributo EAV. Aggiungi un attributo

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

e la query diventa nodosa.

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

Non ribadire il punto, ma cerca di non pensare troppo all'SQL se sei in scadenza.

Altri operatori di confronto Sono sicuro che ti starai chiedendo "cosa succede se desidero qualcosa di diverso da un uguale per query"? Non uguale, maggiore di, minore di, ecc. Anche il secondo parametro del metodo addFieldToFilter ti ha coperto. Supporta una sintassi alternativa in cui, invece di passare una stringa, si passa in un singolo elemento Array.

La chiave di questo array è il tipo di confronto che si desidera effettuare. Il valore associato a quella chiave è il valore per cui si desidera filtrare. Ripeti il ​​filtro sopra, ma con questa sintassi esplicita

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

Richiamo del nostro filtro

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

Come puoi vedere, il secondo parametro è un array PHP. La sua chiave è l'eq, che sta per uguali. Il valore per questa chiave è n2610, che è il valore su cui stiamo filtrando.

Magento ha una serie di questi filtri in lingua inglese che porteranno una lacrima di ricordo (e forse di dolore) a qualsiasi vecchio sviluppatore di perl nel pubblico.

Di seguito sono elencati tutti i filtri, insieme a un esempio dei loro equivalenti 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 maggior parte di questi sono autoesplicativi, ma alcuni meritano un callout speciale

in, nin, find_in_set I condizionali in e nin consentono di passare una matrice di valori. Cioè, la porzione di valore dell'array di filtri può essere essa stessa un array.

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

notnull, null La parola chiave NULL è speciale nella maggior parte dei tipi di SQL. In genere non funziona bene con l'operatore di uguaglianza standard (=). Specificando notnull o null come tipo di filtro otterrai la sintassi corretta per un confronto NULL ignorando qualunque valore passi

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

da - a filtro Questo è un altro formato speciale che infrange la regola standard. Invece di una matrice a singolo elemento, si specifica una matrice a due elementi. Un elemento ha la chiave da, l'altro elemento ha la chiave a. Come indicato dai tasti, questo filtro consente di costruire un intervallo da / a senza preoccuparsi dei simboli maggiore o minore dei simboli

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

I rendimenti di cui sopra

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

AND o OR oppure OR e AND? Infine, arriviamo agli operatori booleani. È il raro momento in cui filtriamo solo per un attributo. Fortunatamente, le Collezioni di Magento ci hanno coperto. È possibile concatenare più chiamate a addFieldToFilter per ottenere un numero di query "AND".

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

Concatenando più chiamate come sopra, creeremo una clausola where che assomiglia al seguente

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

Per quelli di voi che hanno appena alzato la mano, sì, l'esempio sopra restituirà sempre 0 record. Nessuno sku può iniziare con ENTRAMBE un a e un b. Ciò che probabilmente desideriamo qui è una query OR. Questo ci porta ad un altro aspetto confuso del secondo parametro di addFieldToFilter.

Se si desidera creare una query OR, è necessario passare una matrice di matrici di filtri come secondo parametro. Trovo che sia meglio assegnare le singole matrici di filtri alle variabili

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

e quindi assegnare un array di tutte le mie variabili di 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()
        );
}

Nell'interesse di essere espliciti, ecco la suddetta matrice di matrici di filtri.

array($filter_a,$filter_b)

Questo ci darà una clausola WHERE che è simile alla seguente

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

addFilter()consente di filtrare un singolo campo solo per un singolo valore e un tipo. $typepuò essere uno dei seguenti:

  1. "and" (impostazione predefinita) - aggiunge AND $ field = $ value alla clausola WHERE
  2. "or" - aggiunge "OR $ field = $ value alla clausola WHERE

Vedi più dettagli


1
Questo non spiega nulla.
Fabian Schmengler,

Questo non ha alcun senso. Non descrive la differenza di questi metodi
Lindar,


2
La risposta aggiornata viene copiata principalmente da alanstorm.com/magento_collections . Per favore, cita almeno le tue fonti!
Fabian Schmengler,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.