Znalazłem nieco lepszy sposób na zrobienie tego, używając anti-join (Magento 1.9).
Korzyści z tego podejścia
Zaletą tego w porównaniu z pierwotną odpowiedzią jest to, że nie dostaniesz fałszywych wyników pozytywnych, w wyniku czego jest szybszy i mniej podatny na błędy. Załóżmy na przykład, że masz jeden produkt:
- Koszula (w kategoriach: 1 | 2)
Chcesz „znaleźć wszystkie produkty, których nie ma category 3
, a następnie dodać je do category 3
” . Więc uruchom NOT IN
zapytanie, które zwróci dwa wiersze (name | category_id)
:
1. "Shirt" | 1
2. "Shirt" | 2
Nic wielkiego, Magento nadal zwróci tylko pierwszy wynik, a następnie go dodasz. Z wyjątkiem ! Przy drugim uruchomieniu tego zapytania masz takie same wyniki:
1. "Shirt" | 1
2. "Shirt" | 2
A Magento powie ci, że nadal nie dodałeś tej koszuli category 3
. Dzieje się tak, ponieważ gdy produkt należy do wielu kategorii, będzie miał wiele wierszy w tabeli „katalog_produktu_produktu” . I LEFT JOIN
zwróci wiele wyników.
Jest to niepożądane, ponieważ
- Zestaw wyników będzie większy niż potrzeba, co zużyje więcej pamięci niż to konieczne. Zwłaszcza jeśli masz bardzo duży zapas tysięcy przedmiotów.
- Będziesz musiał wykonać dodatkowe sprawdzenie w PHP, aby ustalić, czy wyniki są fałszywie dodatnie (np.
in_array($categoryThree, $product->getCategories())
), Co oznacza, że będziesz przechodził przez niepotrzebne wyniki. Spowoduje to spowolnienie skryptu / kodu, szczególnie przy dużych zapasach.
Rozwiązanie
// 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]
]);
Wygenerowane zapytanie SQL będzie wyglądać następująco:
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`;
Wyjaśnienie:
Biorąc pod uwagę tabele relacji produktu i produktu <=> kategorii:
katalog_product_entity
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
katalog_kategoria_produkt
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
Twoje zapytanie brzmi: „daj mi wszystkie wiersze w „ katalog_produktu_produktu ” i wklej w kolumnie„ kategoria_id ”z „ katalogu_kategorii_produktu ” . Następnie podaj mi tylko wiersze, które id_kategorii = 124” .
Ponieważ jest to lewe łączenie, zawsze będzie miało wiersze z „katalog_produktu_występ” . W przypadku wierszy, których nie można dopasować, będą to NULL
:
Wynik
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
Stamtąd zapytanie mówi: „ok, teraz daj mi wszystko, gdzie id_kategorii ma wartość NULL” .