Bagaimana cara menyaring produk TIDAK dalam kategori?

10

Ini kode saya:

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

Saya ingin mendapatkan semua produk yang tidak ada dalam daftar id kategori tetapi kode saya tidak memberikan hasil yang diharapkan. Tolong tunjukkan saya jalan, terima kasih.

Lan Nguyen
sumber
apa hasil yang Anda harapkan dibandingkan hasil yang Anda dapatkan?
ahnbizcad

Jawaban:

16

Anda harus bergabung dengan tabel yang memuat kategori / hubungan produk.

Variasi koleksi yang saya gunakan untuk menemukan semua produk DI daftar kategori harus melakukan trik untuk Anda:

(belum diuji, tetapi seharusnya membuat Anda di jalur yang benar)

$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/

ProxiBlue
sumber
1
Ini tampaknya tidak berfungsi jika suatu produk muncul di lebih dari satu kategori. 'Nin' hanya mengecualikan satu baris itu, tetapi ID produk masih akan muncul untuk kategori lainnya.
greatwitenorth
Hai, Seperti kode berdiri, ini berfungsi 100%. Kode yang ditulis akan mengecualikan produk sesuai daftar kategori yang diberikan dalam variabel $ catIds. Jika produk muncul di lebih dari satu kategori, dan kategori itu tidak termasuk dalam daftar pengecualian, maka akan muncul. Kesalahannya adalah bahwa Anda tidak mengecualikan semua kategori. Kode tidak dapat secara ajaib (seperti yang tertulis) tahu apa kategori lain yang muncul di produk>
ProxiBlue
Terdengar lebih seperti Anda menginginkan cara untuk mengecualikan id produk, yang dapat Anda peroleh dengan mudah dari kode di atas. Tep pertama adalah untuk bertukar ke IN, kemudian mendapatkan Id produk yang dihasilkan dari permintaan ini, dan kemudian menggunakannya dalam koleksi baru untuk produk, tidak termasuk produk yang diberikan oleh id. Bahkan lebih baik adalah menjadikannya subquery dari koleksi produk yang tidak termasuk id produk.
ProxiBlue
5

Kode berikut akan bekerja untuk Anda:

$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('*');
SoftProdigy
sumber
1

Saya telah menemukan cara yang agak lebih baik untuk melakukan ini, menggunakan anti-gabung (Magento 1.9).

Manfaat dari pendekatan ini

Manfaat dari ini daripada jawaban awal adalah bahwa Anda tidak akan mendapatkan hasil positif yang salah, dan hasilnya lebih cepat dan lebih sedikit kemungkinan kesalahan. Misalnya, anggap Anda memiliki satu produk:

  1. Kemeja (dalam kategori: 1 | 2)

Anda ingin "menemukan semua produk yang tidak ada category 3, lalu menambahkannya ke category 3" . Jadi Anda menjalankan NOT INkueri, dan itu akan mengembalikan dua baris (name | category_id):

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

Bukan masalah besar, Magento masih akan hanya mengembalikan hasil pertama, dan kemudian Anda menambahkannya. Kecuali ! Saat kedua kueri ini dijalankan, Anda memiliki hasil yang sama:

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

Dan Magento akan memberi tahu Anda bahwa Anda belum menambahkan kemeja ini category 3. Ini karena ketika suatu produk termasuk dalam beberapa kategori, mereka akan memiliki banyak baris dalam tabel "catalog_product_entity" . Sehingga LEFT JOINakan mengembalikan beberapa hasil.

Ini tidak diinginkan karena

  1. Kumpulan hasil Anda akan lebih besar dari yang dibutuhkan, yang akan menggunakan lebih banyak memori daripada yang diperlukan. Apalagi jika Anda memiliki persediaan ribuan item yang sangat besar.
  2. Anda perlu melakukan pemeriksaan tambahan dalam PHP untuk menentukan apakah hasilnya positif palsu (misalnya, in_array($categoryThree, $product->getCategories())), artinya Anda akan mengulang hasil yang tidak perlu. Ini akan membuat skrip / kode Anda lebih lambat, terutama dengan inventaris besar.

Larutan

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

Kueri SQL yang dihasilkan akan terlihat seperti:

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

Penjelasan:

Diberikan produk dan tabel hubungan kategori produk <=>:

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

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

Kueri Anda mengatakan, "beri saya semua baris dalam " catalog_product_entity " , dan rekatkan pada kolom" category_id "dari " catalog_category_product " . Lalu beri saya baris yang category_id = 124" .

Karena ini adalah join kiri, ia akan selalu memiliki baris dari "catalog_product_entity" . Untuk setiap baris yang tidak dapat dicocokkan, itu akan menjadi NULL:

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

Dari sana, kueri kemudian berkata, "ok, sekarang berikan saya semua tempat di mana category_id adalah NULL" .

SteveFromAkuntan
sumber
1

Tidak semudah kelihatannya.

Berikut adalah opsi berbasis-GROUP_CONCAT karena batas defaultnya (1024 tapi tentu saja dapat ditingkatkan) harusnya oke dengan ID kategori produk dipisahkan dengan koma yang ditetapkan.

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

Juga (jika Anda tidak suka GROUP_CONCAT), Anda dapat menggunakan WHERE product_id TIDAK DI subquery dari ID produk yang sebenarnya dalam kategori yang perlu Anda kecualikan (tidak berikan di sini).

Pendekatan anti-gabung dari jawaban lain juga akan berhasil. Tetapi dalam hal ini Anda tidak dapat dengan mudah menambahkan kondisi tambahan.

Anton Suslov
sumber