Jak filtrować produkty NIE W kategoriach?


10

Oto mój kod:

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

Chcę uzyskać wszystkie produkty, których nie ma na liście identyfikatorów kategorii, ale mój kod nie dał oczekiwanego rezultatu. Proszę, pokaż mi drogę, dzięki.


jaki był oczekiwany wynik w porównaniu z wynikami, które uzyskałeś?
ahnbizcad

Odpowiedzi:


16

Musisz dołączyć do tabeli zawierającej relacje kategoria / produkt.

Odmiana kolekcji, której używam do znalezienia wszystkich produktów NA liście kategorii, powinna zrobić dla ciebie lewę:

(niesprawdzone, ale powinno doprowadzić cię na właściwą ścieżkę)

$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
Nie działa to, jeśli produkt występuje w więcej niż jednej kategorii. „Nin” po prostu wyklucza ten jeden wiersz, ale identyfikator produktu będzie nadal pojawiał się w innych kategoriach.
greatwitenorth,

Cześć, Jak stoi kod, działa w 100%. Napisany kod wyklucza produkt zgodnie z podaną listą kategorii w zmiennej $ catIds. Jeśli produkt występuje w więcej niż jednej kategorii, a ta kategoria nie znajduje się na liście wykluczeń, to pojawi się. Wina polega na tym, że nie wykluczasz wszystkich kategorii. Kod nie może magicznie (jak jest napisane) wiedzieć, w jakich innych kategoriach pojawia się produkt>
ProxiBlue

Brzmi bardziej, jakbyś chciał wykluczyć według identyfikatorów produktów, które możesz łatwo uzyskać z powyższego kodu. Pierwszym krokiem byłoby zamiana na IN, następnie pobranie identyfikatorów produktu wynikających z tego zapytania, a następnie użycie tego w nowej kolekcji produktów, z wyłączeniem danych produktów według identyfikatora. Jeszcze lepiej jest uczynić z niego podzapytanie kolekcji produktów, które jest wykluczane według identyfikatorów produktów.
ProxiBlue,

5

Poniższy kod będzie działał dla Ciebie:

$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

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:

  1. 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 INzapytanie, 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 JOINzwróci wiele wyników.

Jest to niepożądane, ponieważ

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


1

Nie tak łatwe, jak mogłoby się wydawać.

Oto opcja oparta na GROUP_CONCAT, ponieważ jej domyślny limit (1024, ale oczywiście można go zwiększyć) powinien być w porządku z identyfikatorami kategorii produktów oddzielonymi przecinkami.

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

Ponadto (jeśli nie podoba Ci się GROUP_CONCAT) możesz użyć GDZIE NIE ID_produktu w podzapytaniu o identyfikatorze produktu faktycznie należy do kategorii, które musisz wykluczyć (nie podajemy go tutaj).

Działa również podejście zapobiegające dołączeniu z innej odpowiedzi . Ale w tym przypadku nie można łatwo dodać dodatkowych warunków.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.