addFilter против addFieldToFilter

19

В коллекции Magento есть два метода фильтрации:

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

Кажется, что оба метода добавляют условие условия Zend_Db_Select. И какие преимущества это addFilterдает? Когда я должен использовать это вместо addFieldToFilter?

Lindar
источник

Ответы:

49

Хорошо, давайте рассмотрим их. Первое отличие в том, что addFilter()оно более общее, а не специфичное для базы данных. Он также используется Varien_Directory_Collectionдля фильтрации по имени файла. Но для этого ответа я собираюсь сосредоточиться на Varien_Data_Collection_Db.

Эти методы имеют другую сигнатуру, которая addFilterкажется менее гибкой, но вы увидите, что она также имеет свои преимущества:

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)

параметры

addFieldToFilter () может принимать массив полей с массивом условий или одно поле с одним условием:

  • addFieldToFilter('field', 'value')

    Результаты в: field=value

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

    Результаты в: field1=value1 OR field2=value2

Каждое условие может быть:

  • одно скалярное значение (как 'value1'и 'value2'выше)
  • массив в виде [ operator => value ]
  • Zend_Db_Exprобъект
  • массив условий, которые объединяются с "ИЛИ" (да, это рекурсивно)

Этот, особенно синтаксис «operator => value», задокументирован в коде Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- запомните, я довольно часто их ищу:

 * 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

В операторе from/ есть дополнительная недокументированная функция to:

  • с ['from' => $dateFrom, 'to' => $dateTo, 'date' => true]то $dateFromи $dateToзначения будут обрабатываться как даты. Они могут быть в любой форме, которая принятаVarien_Date::formatDate()
  • если вам нужна функция разбора даты, но только для сравнения одного из <=или >=, вы можете опустить либо 'from'или 'to'.
  • 'datetime' => trueтакже должен работать и включать время, а не только день, но есть ошибка в Varien_Db_Adapter_Pdo_Mysql :: _ prepareSqlDateCondition () (отсутствует $includeTimestampпараметр), которая заставляет datetimeработать так же, как и date. Оба включают время. Так что если вам нужно сравнить только по дате, добавьте 00:00:00к fromдате и 23:59:59к toдате.

Полевое картографирование

Метод использует отображение поля. Отображения полей могут быть определены в конкретных классах коллекций для создания псевдонимов полей. Вот пример из коллекции продуктов:

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

параметры

addFilter()позволяет фильтровать только одно поле по одному значению и типу . $typeможет быть любым из:

  • «и» (по умолчанию) - добавляет AND $field=$valueк предложению WHERE (конечно, с правильным цитированием)
  • «или» - добавляет "OR $field=$valueк предложению WHERE (то же самое)
  • «строка» - добавляет AND $valueк предложению WHERE (т. е. $ value может быть произвольным выражением SQL)
  • «public» - использует отображение поля и _getConditionSql(), аналогично addFieldToFilter(). Это делает его почти таким же мощным, отсутствует только возможность добавить несколько фильтров для разных полей в сочетании с ИЛИ.

В Varien_Data_Collection_Db::_renderFilters()вы можете увидеть, как они обрабатываются.

растяжимость

Есть одно важное отличие, которое является преимуществом addFilter(). Он собирает фильтры для применения $this->_filters()и добавляет их только к Zend_Db_Selectобъекту запроса непосредственно перед загрузкой коллекции. addFieldToFilter()с другой стороны, немедленно манипулирует объектом запроса.

Это позволяет вам манипулировать или удалять фильтры, которые уже были добавлены. Коллекция Varien не имеет интерфейса для этого, вы должны реализовать это в своей пользовательской коллекции. Существует метод перехвата, _renderFiltersBefore()который вы можете переопределить.

Фабиан Шменглер
источник
У меня есть один вопрос, который мы можем использовать addFilterс attributes?
Муртуза Забуавала
@MurtuzaZabuawala нет, его нельзя использовать для атрибутов EAV
Фабиан Шменглер
Спасибо за этот ответ, Фабиан, мне тоже понравился пост вашего сайта об этом, но какое значение может иметь $ field в addFilter? я пытаюсь использовать функцию addFilter, чтобы отфильтровывать только те продукты, которые относятся к категории, в которой работает модуль
Джон
AFAIK невозможен, поскольку категории не являются атрибутами, а связаны с продуктами в отдельной таблице. Не могу дать вам решение на моей голове, извините
Фабиан Шменглер
Спасибо за ответ, не беспокойтесь, если я найду способ обойти это, я обновлю здесь с моим решением
Джон
2

В коллекции Magento есть два метода фильтрации:

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter ($ field, $ condition = null)

Первый параметр addFieldToFilter- это атрибут, по которому вы хотите фильтровать. Второе - это значение, которое вы ищете. Вот мы добавляем skuфильтр для значения n2610.

Второй параметр также можно использовать для указания типа фильтрации, которую вы хотите выполнить. Здесь все становится немного сложнее, и стоит углубиться в него немного глубже.

Так что по умолчанию следующее

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

(по существу) эквивалентно

WHERE sku = "n2610"

Взгляните на себя. Запуск следующего

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

будет давать

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

Имейте в виду, это может быстро усложниться, если вы используете атрибут EAV. Добавить атрибут

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

и запрос становится грубым.

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

Не задумываюсь над этим, но стараюсь не слишком задумываться о SQL, если вы находитесь в крайнем сроке.

Другие операторы сравнения Я уверен, что вы задаетесь вопросом «а что, если я хочу что-то, кроме равенства по запросу»? Не равно, больше, меньше, и т. Д. Второй параметр метода addFieldToFilter также охватил вас. Он поддерживает альтернативный синтаксис, в котором вместо передачи строки вы передаете один элемент Array.

Ключ этого массива - это тип сравнения, который вы хотите сделать. Значение, связанное с этим ключом, является значением, по которому вы хотите фильтровать. Давайте повторим вышеупомянутый фильтр, но с этим явным синтаксисом

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

Вызов нашего фильтра

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

Как видите, вторым параметром является массив PHP. Его ключ - eq, что означает «равно». Значение этого ключа - n2610, то есть значение, по которому мы фильтруем.

В Magento есть несколько таких английских языковых фильтров, которые принесут слезу памяти (и, возможно, боль) любым старым разработчикам Perl в аудитории.

Ниже перечислены все фильтры, а также пример их 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'

Большинство из них говорят сами за себя, но некоторые заслуживают особого

in, nin, find_in_set Условные выражения in и nin позволяют передавать массив значений. То есть порция значения вашего массива фильтра сама по себе может быть массивом.

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

notnull, null Ключевое слово NULL является особенным в большинстве разновидностей SQL. Как правило, это не будет хорошо работать со стандартным оператором равенства (=). Если в качестве типа фильтра указать notnull или null, вы получите правильный синтаксис для сравнения NULL, игнорируя при этом любое передаваемое вами значение.

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

from - to filter Это еще один специальный формат, который нарушает стандартное правило. Вместо одного элемента массива вы указываете массив из двух элементов. Один элемент имеет ключ от, другой элемент имеет ключ. Как указали клавиши, этот фильтр позволяет вам строить диапазон от / до, не беспокоясь о символах больше или меньше

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

Вышеуказанные урожаи

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

И или ИЛИ, или это ИЛИ и И? Наконец, мы подошли к логическим операторам. Это редкий момент, когда мы фильтруем только по одному атрибуту. К счастью, в коллекциях Magento есть информация. Вы можете объединить несколько вызовов в addFieldToFilter, чтобы получить несколько запросов «И».

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

Объединяя несколько вызовов, как описано выше, мы создадим предложение where, которое выглядит примерно так:

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

Тем из вас, кто только что поднял руку, да, приведенный выше пример всегда будет возвращать 0 записей. Никакое sku не может начинаться с ОБА a и a b. То, что мы, вероятно, хотим здесь, это запрос OR. Это подводит нас к еще одному запутанному аспекту второго параметра addFieldToFilter.

Если вы хотите построить запрос ИЛИ, вам нужно передать массив массивов фильтров в качестве второго параметра. Я считаю, что лучше назначить ваши отдельные массивы фильтров для переменных

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

а затем назначить массив всех моих переменных фильтра

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

В целях ясности, вот вышеупомянутый массив массивов фильтров.

array($filter_a,$filter_b)

Это даст нам предложение WHERE, которое выглядит примерно так:

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

addFilter()позволяет фильтровать только одно поле по одному значению и типу. $typeможет быть любым из:

  1. "and" (по умолчанию) - добавляет AND $ field = $ value к предложению WHERE
  2. "or" - добавляет "OR $ field = $ value к предложению WHERE

Посмотреть подробнее

Абдул
источник
1
Это ничего не объясняет.
Фабиан Шменглер
Это не имеет никакого смысла. Это не описывает разницу этих методов
Линдар
2
Ваш обновленный ответ в основном скопирован с alanstorm.com/magento_collections . Пожалуйста, приведите хотя бы ваши источники!
Фабиан Шменглер