Как фильтровать продукты не в категориях?
-
16-10-2019 - |
Вопрос
Вот мой код:
$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 | 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
вернет несколько результатов.
Это нежелательно, потому что
- Ваш набор результатов будет больше, чем необходимо, что будет использовать больше памяти, чем необходимо. Особенно, если у вас есть очень большой инвентарь из тысяч предметов.
- Вам нужно будет сделать дополнительную проверку в 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 не в подзадном идентификаторах продукта, на самом деле в категориях, которые вам нужно исключить (не давая его здесь).
Анти-младший подход от другого отвечать также будет работать. Но в этом случае вы не можете легко добавить дополнительные условия.