Comment filtrer les produits NON DANS les catégories?


10

Voici mon code:

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

Je veux que tous les produits ne figurent pas dans la liste des identifiants de catégorie, mais mon code n'a pas donné le résultat attendu. Veuillez me montrer le chemin, merci.


quel résultat attendiez-vous par rapport aux résultats que vous avez obtenus?
ahnbizcad

Réponses:


16

Vous devez rejoindre le tableau contenant les relations catégorie / produit.

Une variation de la collection que j'utilise pour trouver tous les produits DANS une liste de catégories devrait faire l'affaire pour vous:

(non testé, mais devrait vous mettre sur la bonne voie)

$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();

réf: http://www.proxiblue.com.au/blog/Collection_of_products_in_all_child_categories/


1
Cela ne semble pas fonctionner si un produit apparaît dans plusieurs catégories. Le «nin» exclut simplement cette ligne, mais l'ID du produit apparaîtra toujours pour les autres catégories.
greatwitenorth

Salut, En l'état, il fonctionne à 100%. Le code écrit exclura le produit selon la liste de catégories donnée dans la variable $ catIds. Si le produit apparaît dans plusieurs catégories et que cette catégorie n'est pas incluse dans la liste d'exclusion, eh bien, il apparaîtra. Le défaut est alors que vous n'excluez pas toutes les catégories. Le code ne peut pas magiquement (comme il est écrit) savoir dans quelles autres catégories le produit apparaît>
ProxiBlue

On dirait plus que vous voulez un moyen d'exclure par les ID de produit, que vous pouvez facilement dériver du code ci-dessus. La première étape serait de basculer vers IN, puis d'obtenir les ID de produit résultant de cette requête, puis de l'utiliser dans une nouvelle collection de produits, en excluant les produits donnés par id. Encore mieux est d'en faire une sous-requête de la collection de produits qui est exclue par les identifiants de produit.
ProxiBlue

5

Le code suivant fonctionnera pour vous:

$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

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:

  1. 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 INrequê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 JOINretournera plusieurs résultats.

Ceci n'est pas souhaitable car

  1. 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.
  2. 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" .


1

Pas aussi simple que cela puisse paraître.

Voici l'option basée sur GROUP_CONCAT car sa limite par défaut (1024 mais pourrait être augmentée bien sûr) devrait être correcte avec les ID de catégorie de produits séparés par des virgules.

$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);
}

De plus (si vous n'aimez pas GROUP_CONCAT), vous pouvez utiliser WHERE product_id NOT IN dans une sous-requête d'un ID de produit sont en fait dans les catégories que vous devez exclure (ne pas les donner ici).

L'approche anti-jointure d'une autre réponse fonctionnera également. Mais dans ce cas, vous ne pouvez pas facilement ajouter des conditions supplémentaires.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.