Come filtrare i prodotti NON IN categorie?


10

Ecco il mio codice:

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

Voglio ottenere tutti i prodotti non nell'elenco degli ID categoria ma il mio codice non ha dato il risultato previsto. Per favore, mostrami la strada, grazie.


qual è stato il risultato che ti aspetti dai risultati ottenuti?
ahnbizcad,

Risposte:


16

Devi unirti alla tabella che contiene le relazioni categoria / prodotto.

Una variante della collezione che utilizzo per trovare tutti i prodotti IN un elenco di categorie dovrebbe fare al caso tuo:

(non testato, ma dovrebbe portarti sulla strada giusta)

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

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


1
Questo non sembra funzionare se un prodotto appare in più di una categoria. Il "nin" esclude semplicemente quella riga, ma l'ID prodotto verrà comunque visualizzato per le altre categorie.
greatwitenorth

Ciao, come sta il codice, funziona al 100%. Il codice scritto escluderà il prodotto secondo l'elenco di categorie indicato nella variabile $ catIds. Se il prodotto appare in più di una categoria e quella categoria non è inclusa nell'elenco di esclusione, apparirà. La colpa quindi è che non si stanno escludendo tutte le categorie. Il codice non può magicamente (come è scritto) sapere in quali altre categorie il prodotto appare>
ProxiBlue

Sembra più che tu voglia un mezzo per escludere dagli ID prodotto, che puoi facilmente ricavare dal codice sopra. Il primo tep sarebbe quello di passare a IN, quindi ottenere gli ID prodotto risultanti da questa query e quindi utilizzarli in una nuova raccolta per i prodotti, escludendo i prodotti dati per id. Ancora meglio è renderlo una sottoquery della raccolta di prodotti che viene esclusa dagli ID prodotto.
ProxiBlue,

5

Il seguente codice funzionerà per te:

$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

Ho trovato un modo un po 'migliore per farlo, usando un anti-join (Magento 1.9).

Vantaggi di questo approccio

Il vantaggio di questo rispetto alla risposta originale è che non otterrai falsi positivi, e di conseguenza è più veloce e meno soggetto a errori. Ad esempio, supponiamo di avere un singolo prodotto:

  1. Camicia (nelle categorie: 1 | 2)

Vuoi "trovare tutti i prodotti non presenti category 3, quindi aggiungerli a category 3" . Quindi esegui una NOT INquery e restituirà due righe (name | category_id):

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

Nessun problema, Magento restituirà comunque solo il primo risultato, e poi lo aggiungerai. Tranne ! La seconda volta che viene eseguita questa query, si ottengono gli stessi risultati:

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

E Magento ti dirà che non hai ancora aggiunto questa maglietta a category 3. Questo perché quando un prodotto appartiene a più categorie, avrà più righe nella tabella "catalog_product_entity" . E così a LEFT JOINrestituirà più risultati.

Questo è indesiderabile perché

  1. Il set di risultati sarà più grande del necessario, il che utilizzerà più memoria del necessario. Soprattutto se si dispone di un inventario molto ampio di migliaia di articoli.
  2. Dovrai effettuare un controllo aggiuntivo in PHP per determinare se i risultati sono falsi positivi (ad esempio, in_array($categoryThree, $product->getCategories())), il che significa che eseguirai il ciclo di risultati non necessari. Questo renderà il tuo script / codice più lento, specialmente con grandi inventari.

Soluzione

// 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 query SQL generata sarà simile a:

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

Spiegazione:

Dati il ​​prodotto e le tabelle di relazione categoria <=> prodotto:

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

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

La tua query sta dicendo "dammi tutte le righe in " catalog_product_entity " e incolla la colonna" category_id "da " catalog_category_product " . Quindi dammi solo le righe che category_id = 124" .

Poiché è un join sinistro, avrà sempre le righe da "catalog_product_entity" . Per tutte le righe che non possono essere abbinate, sarà NULL:

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

Da lì, la query quindi dice "ok, ora dammi tutto dove il category_id è NULL" .


1

Non così facile come potrebbe sembrare.

Ecco l'opzione basata su GROUP_CONCAT poiché il suo limite predefinito (1024 ma potrebbe essere aumentato ovviamente) dovrebbe andare bene con gli ID categoria di prodotti separati da virgole.

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

Inoltre (se non ti piace GROUP_CONCAT) puoi utilizzare DOVE product_id NON IN una sottoquery di un ID prodotto è in realtà nelle categorie che devi escludere (non specificandolo qui).

Funzionerà anche l'approccio anti-join di un'altra risposta . Ma in questo caso non è possibile aggiungere facilmente ulteriori condizioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.