addFilter vs addFieldToFilter

19

La collection Magento a deux méthodes de filtrage:

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

Semble que les deux méthodes ajoutent la condition Zend_Db_Select. Et quels avantages addFilterapporte-t-il? Quand devrais-je l'utiliser à la place de addFieldToFilter?

Lindar
la source

Réponses:

49

OK, examinons-les. La première différence est qu'elle addFilter()est plus générique et non spécifique à la base de données. Il est également utilisé par Varien_Directory_Collectionpour filtrer par nom de fichier. Mais pour cette réponse, je vais me concentrer Varien_Data_Collection_Db.

Ces méthodes ont une signature différente, où addFiltersemble être moins flexible, mais vous verrez qu'elle a aussi ses avantages:

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)

Paramètres

addFieldToFilter () peut prendre un tableau de champs avec un tableau de conditions, ou un seul champ avec une seule condition:

  • addFieldToFilter('field', 'value')

    Résulte en: field=value

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

    Résulte en: field1=value1 OR field2=value2

Chaque condition peut être:

  • une seule valeur scalaire (comme 'value1'et 'value2'au - dessus)
  • un tableau sous la forme [ operator => value ]
  • un Zend_Db_Exprobjet
  • un tableau de conditions qui sont combinées avec "OU" (oui, c'est récursif)

Ceci, en particulier la syntaxe "operator => value" est documentée dans le code à Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- rappelez-vous ceci, je les recherche assez souvent:

 * 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

Il existe une fonctionnalité supplémentaire non documentée dans l' opérateur from/ to:

  • avec ['from' => $dateFrom, 'to' => $dateTo, 'date' => true]les valeurs $dateFromet $dateToseront analysées comme des dates. Ils peuvent être sous toute forme acceptée parVarien_Date::formatDate()
  • si vous avez besoin de la fonction d'analyse de la date mais uniquement pour comparer l'un <=ou >=, vous pouvez omettre l'un 'from'ou l' autre 'to'.
  • 'datetime' => trueest censé fonctionner aussi et inclure l'heure, non seulement le jour, mais il y a un bogue dans Varien_Db_Adapter_Pdo_Mysql :: _ prepareSqlDateCondition () ( $includeTimestampparamètre manquant ) qui fait datetimefonctionner de la même manière que date. Les deux incluent le temps. Donc, si vous devez comparer uniquement par date, ajoutez 00:00:00à la fromdate et 23:59:59à la todate.

Cartographie des champs

La méthode utilise le mappage de champs. Les mappages de champs peuvent être définis dans des classes de collection concrètes pour créer des noms de champs d'alias. Voici un exemple de la collection de produits:

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

Paramètres

addFilter()permet uniquement de filtrer un seul champ par une seule valeur et un type . $typepeut être:

  • "et" (par défaut) - ajoute AND $field=$valueà la clause WHERE (bien sûr avec des guillemets appropriés)
  • "ou" - ajoute "OR $field=$valueà la clause WHERE (idem)
  • "chaîne" - ajoute AND $valueà la clause WHERE (ie $ value peut être une expression SQL arbitraire)
  • "public" - utilise la cartographie des champs et _getConditionSql(), similaire à addFieldToFilter(). Cela le rend presque aussi puissant, il ne manque que la fonctionnalité pour ajouter plusieurs filtres pour différents champs combinés avec OR.

En Varien_Data_Collection_Db::_renderFilters()vous pouvez voir comment ils sont traités.

Extensibilité

Il y a une différence importante qui est un avantage pour addFilter(). Il collecte les filtres à appliquer $this->_filters()et les ajoute uniquement à l' Zend_Db_Selectobjet de requête juste avant le chargement de la collection. addFieldToFilter()d'autre part, manipule immédiatement l'objet de requête.

Cela vous permet de manipuler ou de supprimer des filtres qui ont déjà été ajoutés. La collection Varien ne possède pas d'interface, vous devez l'implémenter dans votre collection personnalisée. Il existe une méthode de raccordement _renderFiltersBefore()que vous pouvez remplacer.

Fabian Schmengler
la source
J'ai une seule question que nous pouvons utiliser addFilteravec attributes?
Murtuza Zabuawala
@MurtuzaZabuawala non, il ne peut pas être utilisé pour les attributs EAV
Fabian Schmengler
Merci pour cette réponse Fabian, j'ai également apprécié la publication de votre site Web à ce sujet, mais quelle valeur $ field peut-il contenir dans addFilter? j'essaie d'utiliser la fonction addFilter pour filtrer uniquement les produits qui sont dans la catégorie dans laquelle le module est exécuté
John
AFAIK impossible car les catégories ne sont pas des attributs mais associées à des produits dans un tableau séparé. Je ne peux pas vous donner une solution sur ma tête, désolé
Fabian Schmengler
Merci d'avoir répondu, pas de soucis, si je trouve un moyen de le contourner, je mettrai à jour ici ma solution
John
2

La collection Magento a deux méthodes pour filtrer différentes

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ champ, $ condition = null)

Le premier paramètre de addFieldToFilterest l'attribut que vous souhaitez filtrer. La seconde est la valeur que vous recherchez. Voici que nous ajoutons un skufiltre pour la valeur n2610.

Le deuxième paramètre peut également être utilisé pour spécifier le type de filtrage que vous souhaitez effectuer. C'est là que les choses se compliquent un peu et valent la peine d'être approfondies.

Donc, par défaut, ce qui suit

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

est (essentiellement) équivalent à

WHERE sku = "n2610"

Regardez vous même. Exécution de ce qui suit

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

donnera

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

Gardez à l'esprit que cela peut se compliquer rapidement si vous utilisez un attribut EAV. Ajouter un attribut

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

et la requête devient noueuse.

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

Ne pas insister sur le point, mais essayez de ne pas trop penser au SQL si vous êtes dans les délais.

Autres opérateurs de comparaison Je suis sûr que vous vous demandez «et si je veux autre chose qu'un égal par requête»? Pas égal, supérieur à, inférieur à, etc. Le deuxième paramètre de la méthode addFieldToFilter vous a également couvert. Il prend en charge une syntaxe alternative où, au lieu de passer dans une chaîne, vous passez dans un seul élément Array.

La clé de ce tableau est le type de comparaison que vous souhaitez faire. La valeur associée à cette clé est la valeur que vous souhaitez filtrer. Refaisons le filtre ci-dessus, mais avec cette syntaxe explicite

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

Appeler notre filtre

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

Comme vous pouvez le voir, le deuxième paramètre est un tableau PHP. Sa clé est eq, qui signifie égal. La valeur de cette clé est n2610, qui est la valeur sur laquelle nous filtrons.

Magento a un certain nombre de ces filtres de langue anglaise qui apporteront une larme de souvenir (et peut-être de la douleur) à tous les vieux développeurs de Perl dans le public.

Vous trouverez ci-dessous tous les filtres, ainsi qu'un exemple de leurs équivalents 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 plupart d'entre elles sont explicites, mais quelques-unes méritent une légende spéciale

in, nin, find_in_set Les conditions in et nin vous permettent de transmettre un tableau de valeurs. Autrement dit, la partie valeur de votre tableau de filtres est elle-même autorisée à être un tableau.

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

notnull, null Le mot-clé NULL est spécial dans la plupart des versions de SQL. Il ne jouera généralement pas bien avec l'opérateur d'égalité standard (=). Si vous spécifiez notnull ou null comme type de filtre, vous obtiendrez la syntaxe correcte pour une comparaison NULL tout en ignorant la valeur que vous transmettez.

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

from - to filter Il s'agit d'un autre format spécial qui enfreint la règle standard. Au lieu d'un seul tableau d'éléments, vous spécifiez un tableau à deux éléments. Un élément a la clé de, l'autre élément a la clé de. Comme les touches l'indiquent, ce filtre vous permet de construire une plage de / vers sans avoir à vous soucier des symboles supérieur et inférieur à

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

Les rendements ci-dessus

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

ET ou OU, ou est-ce que OU et ET? Enfin, nous arrivons aux opérateurs booléens. C'est le moment rare où nous ne filtrons que par un attribut. Heureusement, les collections de Magento nous couvrent. Vous pouvez enchaîner plusieurs appels à addFieldToFilter pour obtenir un certain nombre de requêtes «AND».

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

En enchaînant plusieurs appels comme ci-dessus, nous produirons une clause where qui ressemble à ce qui suit

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

Pour ceux d'entre vous qui viennent de lever la main, oui, l'exemple ci-dessus retournerait toujours 0 enregistrement. Aucun sku ne peut commencer par les deux a et b. Ce que nous voulons probablement ici, c'est une requête OU. Cela nous amène à un autre aspect déroutant du deuxième paramètre addFieldToFilter.

Si vous souhaitez créer une requête OR, vous devez passer un tableau de tableaux de filtres en tant que deuxième paramètre. Je trouve qu'il est préférable d'attribuer vos tableaux de filtres individuels à des variables

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

puis attribuer un tableau de toutes mes variables de filtre

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

Pour être explicite, voici le tableau de tableaux de filtres susmentionné.

array($filter_a,$filter_b)

Cela nous donnera une clause WHERE qui ressemble à ce qui suit

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

addFilter()permet uniquement de filtrer un seul champ par une seule valeur et un type. $typepeut être:

  1. "et" (par défaut) - ajoute la valeur AND $ field = $ à la clause WHERE
  2. "ou" - ajoute "OR $ field = $ value à la clause WHERE

Voir plus de détails

Abdul
la source
1
Cela n'explique rien.
Fabian Schmengler
Cela n'a aucun sens. Il ne décrit pas la différence de ces méthodes
Lindar
2
Votre réponse mise à jour est principalement copiée sur alanstorm.com/magento_collections . Merci de citer au moins vos sources!
Fabian Schmengler