Как фильтровать товары НЕ по категориям?


10

Вот мой код:

$catIds = array(7,8,9);
$collection = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToSelect("*");
    ->addAttributeToFilter('category_ids', array('nin' => $catIds));

Я хочу, чтобы все товары отсутствовали в списке идентификаторов категорий, но мой код не дал ожидаемого результата. Пожалуйста, покажи мне путь, спасибо.


какой результат вы ожидаете по сравнению с результатами, которые вы получили?
ahnbizcad

Ответы:


16

Вы должны присоединиться к таблице, которая содержит отношения категории / продукта.

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

(не проверено, но вы должны быть на правильном пути)

$productCollection = Mage::getResourceModel('catalog/product_collection')
    ->setStoreId(0)
    ->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id=entity_id', null, 'left')
    ->addAttributeToFilter('category_id', array('nin' => $catIds))
    ->addAttributeToSelect('*');

$productCollection->getSelect()->group('product_id')->distinct(true);
$productCollection->load();

ссылка: http://www.proxiblue.com.au/blog/Collection_of_products_in_all_child_categories/


1
Это не работает, если продукт появляется в более чем одной категории. «Nin» просто исключает эту строку, но идентификатор продукта все равно подходит для других категорий.
greatwitenorth

Привет, Поскольку код стоит, он работает на 100%. Написанный код исключит продукт согласно заданному списку категорий в переменной $ catIds. Если продукт представлен в нескольких категориях, и эта категория не включена в список исключений, то он появится. Ошибка в том, что вы не исключаете все категории. Код не может волшебным образом (как написано) знать, в каких других категориях продукт появляется>
ProxiBlue

Звучит так, будто вы хотите исключить средства по идентификаторам продуктов, которые можно легко получить из приведенного выше кода. Первый tep должен был бы переключиться на IN, затем получить идентификаторы продукта, полученные из этого запроса, а затем использовать их в новой коллекции для продуктов, исключая данные продукты по id. Еще лучше сделать это подзапросом коллекции продуктов, исключая идентификаторы продуктов.
ProxiBlue

5

Следующий код будет работать для вас:

$catIds = array(7,8,9);
$_productCollection = Mage::getModel('catalog/product')
                ->getCollection()
                ->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id = entity_id', null, 'left')
                ->addAttributeToFilter('category_id', array('nin' => array('finset' => $catIds)))
                ->addAttributeToSelect('*');

1

Я нашел несколько лучший способ сделать это, используя анти-объединение (Magento 1.9).

Преимущества этого подхода

Преимущество этого по сравнению с оригинальным ответом состоит в том, что вы не получите ложных срабатываний, и в результате он быстрее и менее подвержен ошибкам. Например, предположим, у вас есть один продукт:

  1. Рубашка (в категориях: 1 | 2)

Вы хотите "найти все товары, которых нет в category 3, а затем добавить их в category 3" . Итак, вы запускаете NOT INзапрос, и он возвращает две строки (name | category_id):

1. "Shirt" | 1
2. "Shirt" | 2

Ничего страшного, Magento все равно вернет только первый результат, а затем вы добавите его. Кроме ! Во второй раз, когда этот запрос запускается, вы получаете те же результаты:

1. "Shirt" | 1
2. "Shirt" | 2

И Magento скажет вам, что вы еще не добавили эту рубашку category 3. Это связано с тем, что когда товар принадлежит нескольким категориям, он будет иметь несколько строк в таблице «catalog_product_entity» . И такLEFT JOIN вернет несколько результатов.

Это нежелательно, потому что

  1. Ваш набор результатов будет больше, чем нужно, что потребует больше памяти, чем необходимо. Особенно, если у вас очень большой инвентарь из тысяч предметов.
  2. Вам нужно будет выполнить дополнительную проверку в PHP, чтобы определить, являются ли результаты ложными срабатываниями (например, in_array($categoryThree, $product->getCategories())), что означает, что вы будете перебирать ненужные результаты. Это замедлит ваш скрипт / код, особенно при больших запасах.

Решение

// All products not in this category ID
$notInThisCatId = '123';

$filteredProducts = Mage::getModel('catalog/product')->getCollection();
$filteredProducts
    ->joinField('category_id', 'catalog_category_product', 'category_id', 'product_id=entity_id', ['category_id'=>$notInThisCatId], 'left');
$filteredProducts
    ->addAttributeToFilter('category_id', [
        ['null' => true]
    ]);

Сгенерированный SQL-запрос будет выглядеть так:

SELECT 
    DISTINCT `e`.*, `at_category_id`.`category_id` 
FROM `catalog_product_entity` AS `e`
LEFT JOIN `catalog_category_product` AS `at_category_id` 
    ON (at_category_id.`product_id`=e.entity_id) AND (at_category_id.category_id = '123')
WHERE (at_category_id.category_id IS NULL)
GROUP BY `e`.`entity_id`;

Объяснение:

Учитывая таблицы отношений продукта и категории <=>:

catalog_product_entity +-----------+ | ENTITY_ID | +-----------+ | 423 | | 424 | | 425 | +-----------+

catalog_category_product +-------------+------------+ | CATEGORY_ID | PRODUCT_ID | +-------------+------------+ | 3 | 423 | | 123 | 424 | | 3 | 425 | +-------------+------------+

Ваш запрос говорит: «Дайте мне все строки в « catalog_product_entity » и вставьте столбец« category_id »из « catalog_category_product » . Затем просто дайте мне строки, которые category_id = 124" .

Поскольку это левое соединение, в нем всегда будут строки из catalog_product_entity . Для любых строк, которые не могут быть сопоставлены, это будет NULL:

Результат +-------------+-------------+ | ENTITY_ID | CATEGORY_ID | +-------------+-------------+ | 423 | NULL | | 424 | 123 | | 425 | NULL | +-------------+-------------+

Оттуда запрос говорит: «Хорошо, теперь дай мне все, где category_id равен NULL» .


1

Не так просто, как может показаться.

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

$categoryIdsToExclude = array(1, 2, 4); // Category IDs products should not be in

$collection = Mage::getModel('catalog/product')->getCollection();

$selectCategories = $collection->getConnection()->select();
$selectCategories->from($collection->getTable('catalog/category_product'), array('product_id', 'category_id'));
$mysqlHelper = Mage::getResourceHelper('core');
$mysqlHelper->addGroupConcatColumn(
    $selectCategories,
    'category_ids_set',
    'category_id',
    ','
);
$selectCategories->group('product_id');

$collection->getSelect()->joinLeft(
    array('category_ids_set_table' => new Zend_Db_Expr('(' . $selectCategories->__toString() . ')')),
    'category_ids_set_table.product_id = e.entity_id',
    array('category_ids_set' => 'category_ids_set_table.category_ids_set')
);

foreach ($categoryIdsToExclude as $val) {
    $collection->getSelect()->where('NOT FIND_IN_SET(?, category_ids_set)', $val);
}

Кроме того (если вам не нравится GROUP_CONCAT), вы можете использовать WHERE product_id NOT IN, а подзапрос идентификаторов продукта - фактически IN категории, которые вы должны исключить (не давая этого здесь).

Анти-объединение подход из другого ответа также будет работать. Но в этом случае вы не можете легко добавить дополнительные условия.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.