Wie filtere ich Produkte, die NICHT IN Kategorien sind?


10

Hier ist mein Code:

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

Ich möchte alle Produkte erhalten, die nicht in der Liste der Kategorie-IDs enthalten sind, aber mein Code hat nicht das erwartete Ergebnis geliefert. Bitte zeig mir den Weg, danke.


Was war das erwartete Ergebnis im Vergleich zu den erzielten Ergebnissen?
Ahnbizcad

Antworten:


16

Sie müssen der Tabelle beitreten, die die Kategorie- / Produktbeziehungen enthält.

Eine Variation der Sammlung, mit der ich alle Produkte in einer Liste von Kategorien finde, sollte den Trick für Sie tun:

(ungetestet, sollte dich aber auf den richtigen Weg bringen)

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

ref: http://www.proxiblue.com.au/blog/Collection_of_products_in_all_child_categories/


1
Dies scheint nicht zu funktionieren, wenn ein Produkt in mehr als einer Kategorie angezeigt wird. Die 'nin' schließt diese eine Zeile einfach aus, aber die Produkt-ID wird für die anderen Kategorien weiterhin angezeigt.
Greatwitenorth

Hallo, so wie der Code steht, funktioniert er zu 100%. Der geschriebene Code schließt das Produkt gemäß der angegebenen Kategorieliste in der Variablen $ catIds aus. Wenn das Produkt in mehr als einer Kategorie angezeigt wird und diese Kategorie nicht in der Ausschlussliste enthalten ist, wird es angezeigt. Der Fehler ist dann, dass Sie nicht alle Kategorien ausschließen. Der Code kann nicht auf magische Weise (wie geschrieben) wissen, in welchen anderen Kategorien das Produkt erscheint>
ProxiBlue

Klingt eher so, als ob Sie ein Mittel zum Ausschließen von Produkt-IDs wünschen, das Sie leicht aus dem obigen Code ableiten können. Der erste Schritt wäre, zu IN zu wechseln, dann die aus dieser Abfrage resultierenden Produkt-IDs abzurufen und diese dann in einer neuen Produktkollektion zu verwenden, wobei die angegebenen Produkte nach ID ausgeschlossen werden. Noch besser ist es, es zu einer Unterabfrage der Produktkollektion zu machen, die nach Produkt-IDs ausgeschlossen ist.
ProxiBlue

5

Der folgende Code funktioniert für Sie:

$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

Ich habe einen etwas besseren Weg gefunden, dies mit einem Anti-Join (Magento 1.9) zu tun.

Vorteile dieses Ansatzes

Dies hat gegenüber der ursprünglichen Antwort den Vorteil, dass Sie keine Fehlalarme erhalten und dadurch schneller und weniger fehleranfällig sind. Angenommen, Sie haben ein einzelnes Produkt:

  1. Hemd (in Kategorien: 1 | 2)

Sie möchten "alle Produkte finden, die nicht in sind category 3, und sie dann hinzufügen category 3" . Sie führen also eine NOT INAbfrage aus und es werden zwei Zeilen zurückgegeben (name | category_id):

1. "Shirt" | 1
2. "Shirt" | 2

Keine große Sache, Magento gibt immer noch nur das erste Ergebnis zurück, und dann fügen Sie es hinzu. Außer ! Wenn diese Abfrage zum zweiten Mal ausgeführt wird, erhalten Sie dieselben Ergebnisse:

1. "Shirt" | 1
2. "Shirt" | 2

Und Magento wird Ihnen sagen, dass Sie dieses Shirt noch nicht hinzugefügt haben category 3. Dies liegt daran, dass ein Produkt, das zu mehreren Kategorien gehört, mehrere Zeilen in der Tabelle "catalog_product_entity" enthält . Und so LEFT JOINwird a mehrere Ergebnisse zurückgeben.

Dies ist unerwünscht, weil

  1. Ihre Ergebnismenge ist größer als erforderlich, wodurch mehr Speicher als erforderlich belegt wird. Besonders dann, wenn Sie einen sehr großen Bestand an Tausenden von Artikeln haben.
  2. Sie müssen eine zusätzliche Überprüfung in PHP durchführen, um festzustellen, ob die Ergebnisse falsch positiv sind (z. B. in_array($categoryThree, $product->getCategories())), was bedeutet, dass Sie unnötige Ergebnisse durchlaufen. Dies verlangsamt Ihr Skript / Ihren Code, insbesondere bei großen Lagerbeständen.

Lösung

// 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]
    ]);

Die generierte SQL-Abfrage sieht folgendermaßen aus:

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`;

Erläuterung:

In Anbetracht der Beziehungstabellen für Produkt und Produktkategorie <=>:

catalog_product_entity +-----------+ | ENTITY_ID | +-----------+ | 423 | | 424 | | 425 | +-----------+

catalog_category_product +-------------+------------+ | CATEGORY_ID | PRODUCT_ID | +-------------+------------+ | 3 | 423 | | 123 | 424 | | 3 | 425 | +-------------+------------+

Ihre Abfrage lautet: "Geben Sie mir alle Zeilen in " catalog_product_entity " und fügen Sie sie in die Spalte" category_id "von " catalog_category_product "ein . Geben Sie mir dann einfach die Zeilen" category_id = 124 " .

Da es sich um eine Linksverknüpfung handelt, enthält sie immer die Zeilen von "catalog_product_entity" . Für alle Zeilen, die nicht abgeglichen werden können, gilt Folgendes NULL:

Ergebnis +-------------+-------------+ | ENTITY_ID | CATEGORY_ID | +-------------+-------------+ | 423 | NULL | | 424 | 123 | | 425 | NULL | +-------------+-------------+

Von dort sagt die Abfrage dann: "OK, jetzt gib mir alles, wo die category_id NULL ist" .


1

Nicht so einfach wie es aussehen mag.

Hier ist die GROUP_CONCAT-basierte Option, da das Standardlimit (1024, könnte aber natürlich erhöht werden) mit durch Kommas getrennten Produktkategorie-IDs in Ordnung sein sollte.

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

Außerdem (wenn Sie GROUP_CONCAT nicht mögen) können Sie WHERE product_id NOT IN in einer Unterabfrage einer Produkt-ID verwenden. IDs sind tatsächlich IN Kategorien, die Sie ausschließen müssen (hier nicht angeben).

Der Anti-Join-Ansatz einer anderen Antwort funktioniert ebenfalls. In diesem Fall können Sie jedoch nicht einfach zusätzliche Bedingungen hinzufügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.