Вопрос

Вот мой код:

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

Я хочу получить все продукты не в списке идентификаторов категорий, но мой код не дал ожидаемый результат. Пожалуйста, покажи мне, спасибо.

Это было полезно?

Решение

Вам нужно присоединиться к таблице, в которой содержится отношения категории/продукт.

Вариант коллекции, который я использую для поиска всех продуктов в списке категорий, должен сделать для вас свое дело:

(непроверенный, но должен попасть в правильный путь)

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

Другие советы

Следующий код будет работать для вас:

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

Я нашел несколько лучшего способа сделать это, используя анти-Join (Magento 1.9).

Преимущества этого подхода

Преимущество этого по сравнению с исходным ответом заключается в том, что вы не получите ложных срабатываний, и в результате это быстрее и менее подвержено ошибкам. Например, предположим, у вас есть один продукт:

  1. Рубашка (в категориях: 1 | 2)

Вы хотите "Найдите все продукты не в category 3, затем добавьте их в category 3". Анкет Итак, вы запускаете NOT IN запрос, и он вернет два ряда (name | category_id):

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

Ничего страшного, Magento все равно вернет только первый результат, а затем вы добавите его. Кроме! Во второй раз, когда этот запрос запускается, у вас есть те же результаты:

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

И Magento скажет вам, что вы все еще не добавили эту рубашку в category 3. Анкет Это потому, что когда продукт принадлежит нескольким категориям, у них будет несколько строк в "catalog_product_entity" стол. И так LEFT JOIN вернет несколько результатов.

Это нежелательно, потому что

  1. Ваш набор результатов будет больше, чем необходимо, что будет использовать больше памяти, чем необходимо. Особенно, если у вас есть очень большой инвентарь из тысяч предметов.
  2. Вам нужно будет сделать дополнительную проверку в PHP, чтобы определить, являются ли результаты ложными положительными (например, in_array($categoryThree, $product->getCategories())), то есть вы проберите ненужные результаты. Это сделает ваш скрипт/код медленнее, особенно с большими запасами.

Решение

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

Сгенерированный запрос SQL будет выглядеть как:

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

Объяснение:

Учитывая продукт и продукт <=> Таблицы взаимоотношений категории:

CATALOG_PRODUCT_ENTITY +-----------+ | ENTITY_ID | +-----------+ | 423 | | 424 | | 425 | +-----------+

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

Ваш запрос говорит, "Дай мне все ряды в "catalog_product_entity", и вставьте столбец "Category_id" из "catalog_category_product". Анкет Тогда просто дайте мне ряды, которые Category_id = 124 ".

Поскольку это левое соединение, у него всегда будут ряды "catalog_product_entity". Анкет Для любых рядов, которые нельзя соответствовать, это будет NULL:

Результат +-------------+-------------+ | ENTITY_ID | CATEGORY_ID | +-------------+-------------+ | 423 | NULL | | 424 | 123 | | 425 | NULL | +-------------+-------------+

Оттуда запрос говорит: «Хорошо, теперь дай мне все, где категория_ид ноль».

Не так просто, как может выглядеть.

Вот вариант на основе Group_Concat, поскольку он является лимитом по умолчанию (1024, но, конечно, может быть увеличен), должен быть в порядке с идентификаторами категории продукта, разделенными набора запятых.

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

Также (если вам не нравится Group_concat), вы можете использовать там, где Product_id не в подзадном идентификаторах продукта, на самом деле в категориях, которые вам нужно исключить (не давая его здесь).

Анти-младший подход от другого отвечать также будет работать. Но в этом случае вы не можете легко добавить дополнительные условия.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с magento.stackexchange
scroll top