J'ai trouvé un moyen un peu meilleur de le faire, en utilisant un anti-jointure (Magento 1.9).
Avantages de cette approche
L'avantage de cela par rapport à la réponse d'origine est que vous n'obtiendrez pas de faux positifs, ce qui est plus rapide et moins sujet aux erreurs. Par exemple, supposons que vous ayez un seul produit:
- Chemise (dans les catégories: 1 | 2)
Vous voulez "trouver tous les produits qui ne s'y trouvent pas category 3
, puis les ajouter à category 3
" . Vous exécutez donc une NOT IN
requête et elle renverra deux lignes (name | category_id)
:
1. "Shirt" | 1
2. "Shirt" | 2
Pas de problème, Magento ne renverra toujours que le premier résultat, puis vous l'ajouterez. Sauf ! La deuxième fois que cette requête est exécutée, vous obtenez les mêmes résultats:
1. "Shirt" | 1
2. "Shirt" | 2
Et Magento vous dira que vous n'avez pas encore ajouté cette chemise category 3
. En effet, lorsqu'un produit appartient à plusieurs catégories, il y aura plusieurs lignes dans la table "catalog_product_entity" . Et donc un LEFT JOIN
retournera plusieurs résultats.
Ceci n'est pas souhaitable car
- Votre jeu de résultats sera plus grand que nécessaire, ce qui consommera plus de mémoire que nécessaire. Surtout si vous avez un très grand inventaire de milliers d'articles.
- Vous devrez effectuer une vérification supplémentaire en PHP pour déterminer si les résultats sont des faux positifs (par exemple,
in_array($categoryThree, $product->getCategories())
), ce qui signifie que vous parcourrez les résultats inutiles. Cela rendra votre script / code plus lent, surtout avec de gros inventaires.
Solution
// 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]
]);
La requête SQL générée ressemblera à ceci:
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`;
Explication:
Compte tenu des tables de relation produit et <=> catégorie de produit:
catalog_product_entity
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
catalog_category_product
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
Votre requête dit "donnez-moi toutes les lignes dans " catalog_product_entity " et collez-la dans la colonne" category_id "de " catalog_category_product " . Ensuite, donnez-moi simplement les lignes que category_id = 124" .
Parce que c'est une jointure gauche, elle aura toujours les lignes de "catalog_product_entity" . Pour toutes les lignes qui ne peuvent pas être rapprochées, ce sera NULL
:
Résultat
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
À partir de là, la requête dit alors "ok, donnez-moi maintenant tout où le category_id est NULL" .