addFilter vs addFieldToFilter

19

Kolekcja Magento ma dwie metody filtrowania:

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

Wydaje się, że obie metody dodają warunek Zend_Db_Select. A jakie korzyści addFilterprzynosi? Kiedy powinienem go użyć zamiast addFieldToFilter?

Lindar
źródło

Odpowiedzi:

49

OK, sprawdźmy je. Pierwszą różnicą jest to, że addFilter()jest bardziej ogólna, a nie specyficzna dla bazy danych. Służy również Varien_Directory_Collectiondo filtrowania według nazwy pliku. Ale na tę odpowiedź zamierzam się skupić Varien_Data_Collection_Db.

Metody te mają inną sygnaturę, która addFilterwydaje się mniej elastyczna, ale zobaczysz, że ma ona również swoje zalety:

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)

Parametry

addFieldToFilter () może przyjmować tablicę pól z tablicą warunków lub pojedyncze pole z jednym warunkiem:

  • addFieldToFilter('field', 'value')

    Prowadzi do: field=value

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

    Prowadzi do: field1=value1 OR field2=value2

Każdy warunek może być:

  • pojedyncza wartość skalarna (jak 'value1'i 'value2'powyżej)
  • tablica w formie [ operator => value ]
  • Zend_Db_Exprprzedmiot
  • tablica warunków, które są połączone z „LUB” (tak, to rekurencyjne)

Ta, zwłaszcza składnia „operator => wartość” jest udokumentowana w kodzie pod adresem Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- pamiętaj o tym, często je sprawdzam:

 * 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

Istnieje dodatkowa nieudokumentowana funkcja w from/ tooperator:

  • z i['from' => $dateFrom, 'to' => $dateTo, 'date' => true]$dateFrom$dateTo wartości będą przetwarzane tak terminach. Mogą być w dowolnej formie zaakceptowanej przezVarien_Date::formatDate()
  • jeśli potrzebujesz datę analizowania funkcji ale tylko porównać jedną <=lub >=można pominąć albo 'from'albo 'to'.
  • 'datetime' => truema również działać i uwzględniać czas, nie tylko dzień, ale jest błąd w Varien_Db_Adapter_Pdo_Mysql :: _ preparSqlDateCondition () (brak $includeTimestampparametru), który sprawia, że datetimedziała tak samo jak date. Oba obejmują czas. Więc jeśli trzeba porównać tylko według daty, dodać 00:00:00do fromdaty i 23:59:59do totej pory.

Mapowanie pola

Metoda wykorzystuje mapowanie pól. Odwzorowania pól można zdefiniować w konkretnych klasach kolekcji, aby utworzyć aliasy nazw pól. Oto przykład z kolekcji produktów:

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

Parametry

addFilter()pozwala tylko na filtrowanie pojedynczego pola według pojedynczej wartości i typu . $typemoże być dowolnym z:

  • „i” (domyślnie) - dodaje AND $field=$valuedo klauzuli WHERE (oczywiście z prawidłowym cytowaniem)
  • „lub” - dodaje "OR $field=$valuedo klauzuli WHERE (podobnie)
  • „ciąg” - dodaje AND $valuedo klauzuli WHERE (tzn. wartość $ może być dowolnym wyrażeniem SQL)
  • „public” - używa mapowania pól i _getConditionSql()podobnie jak addFieldToFilter(). To sprawia, że ​​jest prawie tak samo wydajny, brakuje tylko funkcji dodawania wielu filtrów dla różnych pól w połączeniu z OR.

W Varien_Data_Collection_Db::_renderFilters() można zobaczyć w jaki sposób są one przetwarzane.

Rozciągliwość

Jest jedna ważna różnica, która jest zaletą addFilter(). Zbiera filtry, które mają zostać zastosowane, $this->_filters()i dodaje je tylko do Zend_Db_Selectobiektu zapytania tuż przed załadowaniem kolekcji. addFieldToFilter()z drugiej strony natychmiast manipuluje obiektem zapytania.

Umożliwia to manipulowanie lub usuwanie filtrów, które zostały już dodane. Kolekcja Varien nie ma dla niej interfejsu, musisz zaimplementować to w swojej kolekcji niestandardowej. Istnieje metoda haka _renderFiltersBefore(), którą można zastąpić.

Fabian Schmengler
źródło
Mam jedno pytanie możemy korzystać addFilterz attributes?
Murtuza Zabuawala,
@MurtuzaZabuawala nie, nie można go używać do atrybutów EAV
Fabian Schmengler
Dziękuję za tę odpowiedź Fabian, podobał mi się również post na twojej stronie na ten temat, ale jaką wartość może mieć $ field w addFilter? Próbuję użyć funkcji addFilter do filtrowania tylko produktów należących do kategorii, w której działa moduł
John
AFAIK nie jest możliwe, ponieważ kategorie nie są atrybutami, ale są powiązane z produktami w osobnej tabeli. Przepraszam, nie mogę dać ci rozwiązania
Fabian Schmengler
Dzięki za odpowiedź, nie martw się, jeśli znajdę rozwiązanie, zaktualizuję tutaj swoje rozwiązanie
John
2

Kolekcja Magento ma dwie metody filtrowania poniżej

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ field, $ condition = null)

Pierwszy parametr addFieldToFilterto atrybut, według którego chcesz filtrować. Druga to wartość, której szukasz. Oto dodajemy skufiltr wartości n2610.

Drugi parametr może być również użyty do określenia rodzaju filtrowania, które chcesz wykonać. W tym miejscu sprawy stają się trochę skomplikowane i warto je zagłębić nieco głębiej.

Więc domyślnie następujące

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

jest (zasadniczo) równoważny z

WHERE sku = "n2610"

Sprawdź sam. Uruchamianie następujących

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

ustąpi

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

Pamiętaj, że może to szybko się skomplikować, jeśli używasz atrybutu EAV. Dodaj atrybut

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

i zapytanie staje się niepoprawne.

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

Nie spiesz się, ale staraj się nie myśleć za dużo o SQL, jeśli dotrzymasz terminu.

Inne operatory porównania Jestem pewien, że zastanawiasz się „co jeśli chcę czegoś innego niż równa się zapytaniem”? Nie równy, większy niż, mniejszy itp. Drugi parametr metody addFieldToFilter również tam uwzględniał. Obsługuje alternatywną składnię, w której zamiast przekazywać ciąg, przekazujesz pojedynczy element Array.

Kluczem tej tablicy jest rodzaj porównania, które chcesz wykonać. Wartość powiązana z tym kluczem to wartość, według której chcesz filtrować. Ponówmy powyższy filtr, ale z tą jawną składnią

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

Wywoływanie naszego filtra

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

Jak widać, drugim parametrem jest tablica PHP. Jego kluczem jest eq, co oznacza równość. Wartość tego klucza to n2610, czyli wartość, którą filtrujemy.

Magento ma wiele takich anglojęzycznych filtrów, które przyniosą łzę pamięci (i być może bólu) wszystkim starym programistom Perla na widowni.

Poniżej wymienione są wszystkie filtry wraz z przykładem ich odpowiedników 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'

Większość z nich jest oczywista, ale kilka zasługuje na specjalne objaśnienie

in, nin, find_in_set Warunki warunkowe in i nin pozwalają przekazać tablicę wartości. Oznacza to, że część wartości tablicy filtrów może sama być tablicą.

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

notnull, null Słowo kluczowe NULL jest wyjątkowe w większości odmian SQL. Zwykle nie gra dobrze ze standardowym operatorem równości (=). Podanie notnull lub null jako typu filtru zapewni poprawną składnię dla porównania NULL, ignorując przy tym jakąkolwiek przekazywaną wartość

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

from - to filter Jest to kolejny specjalny format, który łamie standardową regułę. Zamiast tablicy z jednym elementem określasz tablicę z dwoma elementami. Jeden element ma klucz, a drugi ma klucz do. Jak wskazano na kluczach, ten filtr pozwala konstruować zakres od / do bez martwienia się o symbole większe i mniejsze niż

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

Powyższe daje

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

AND lub OR, czy to jest OR i AND? Wreszcie dochodzimy do operatorów boolowskich. To rzadki moment, w którym filtrujemy tylko według jednego atrybutu. Na szczęście kolekcje Magento nas obejmują. Możesz połączyć wiele połączeń w addFieldToFilter, aby uzyskać szereg zapytań „ORAZ”.

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

Łącząc ze sobą wiele wywołań, jak powyżej, stworzymy klauzulę where, która wygląda mniej więcej tak:

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

Dla tych z was, którzy właśnie podnieśli rękę, tak, powyższy przykład zawsze zwróci 0 rekordów. Żadne SKU nie może zaczynać się od OBA i a i b. Prawdopodobnie chcemy tutaj zapytania OR. To prowadzi nas do kolejnego mylącego aspektu drugiego parametru addFieldToFilter.

Jeśli chcesz zbudować zapytanie OR, musisz przekazać tablicę tablic filtrów jako drugi parametr. Uważam, że najlepiej przypisać indywidualne tablice filtrów do zmiennych

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

a następnie przypisz tablicę wszystkich moich zmiennych filtrów

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

Dla jasności, oto wspomniana tablica tablic filtrujących.

array($filter_a,$filter_b)

To da nam klauzulę WHERE, która wygląda mniej więcej tak:

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

addFilter()pozwala tylko na filtrowanie pojedynczego pola według pojedynczej wartości i typu. $typemoże być dowolnym z:

  1. „i” (domyślnie) - dodaje AND AND field = $ wartość do klauzuli WHERE
  2. „lub” - dodaje „OR $ field = $ wartość do klauzuli WHERE

Zobacz więcej szczegółów

Abdul
źródło
1
To niczego nie wyjaśnia.
Fabian Schmengler
To nie ma sensu. Nie opisuje różnicy tych metod
Lindar,
2
Twoja zaktualizowana odpowiedź jest w większości kopiowana z alanstorm.com/magento_collections . Przytacz przynajmniej swoje źródła!
Fabian Schmengler