Я знайшов дещо кращий спосіб зробити це, використовуючи анти-приєднання (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`;
Пояснення:
З огляду на таблицю відносин продукту та продукту <=> категорії:
каталог_продукт_істотність
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
каталог_категорія_продукт
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
Ваш запит говорить: "дайте мені всі рядки в " catalog_product_entity " , і вставте в стовпець" category_id "з " catalog_category_product " . Потім просто дайте мені рядки, що категорія_id = 124" .
Оскільки це ліве з'єднання, воно завжди матиме рядки з "catalog_product_entity" . Для будь-яких рядків, які неможливо зіставити, це NULL
:
Результат
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
Звідси запит каже: "нормально, тепер дайте мені все, де категорія_id - NULL" .