Я нашел несколько лучший способ сделать это, используя анти-объединение (Magento 1.9).
Преимущества этого подхода
Преимущество этого по сравнению с оригинальным ответом состоит в том, что вы не получите ложных срабатываний, и в результате он быстрее и менее подвержен ошибкам. Например, предположим, у вас есть один продукт:
- Рубашка (в категориях: 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
вернет несколько результатов.
Это нежелательно, потому что
- Ваш набор результатов будет больше, чем нужно, что потребует больше памяти, чем необходимо. Особенно, если у вас очень большой инвентарь из тысяч предметов.
- Вам нужно будет выполнить дополнительную проверку в 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» .