Eu encontrei uma maneira um pouco melhor de fazer isso, usando um anti-join (Magento 1.9).
Benefícios desta abordagem
O benefício disso em relação à resposta original é que você não obterá falsos positivos e, como resultado, é mais rápido e menos propenso a erros. Por exemplo, suponha que você tenha um único produto:
- Camisa (nas categorias: 1 | 2)
Você deseja "encontrar todos os produtos que não estão dentro category 3
e adicioná-los a category 3
" . Então, você executa uma NOT IN
consulta e ela retornará duas linhas (name | category_id)
:
1. "Shirt" | 1
2. "Shirt" | 2
Não é grande coisa, o Magento ainda retornará apenas o primeiro resultado e você o adicionará. Exceto ! Na segunda vez que essa consulta é executada, você tem os mesmos resultados:
1. "Shirt" | 1
2. "Shirt" | 2
E o Magento lhe dirá que você ainda não adicionou esta camisa category 3
. Isso ocorre porque quando um produto pertence a várias categorias, ele tem várias linhas na tabela "catalog_product_entity" . E assim um LEFT JOIN
retornará vários resultados.
Isso é indesejável porque
- Seu conjunto de resultados será maior que o necessário, o que consumirá mais memória do que o necessário. Especialmente se você tiver um estoque muito grande de milhares de itens.
- Você precisará fazer uma verificação adicional no PHP para determinar se os resultados são falsos positivos (por exemplo,
in_array($categoryThree, $product->getCategories())
), o que significa que você percorrerá resultados desnecessários. Isso tornará seu script / código mais lento, especialmente em grandes inventários.
Solução
// 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]
]);
A consulta SQL gerada será semelhante 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`;
Explicação:
Dadas as tabelas de relacionamento de produto e categoria de produto <=>:
catalog_product_entity
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
catalog_category_product
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
Sua consulta está dizendo "dê-me todas as linhas em " catalog_product_entity " e cole na coluna" category_id "de " catalog_category_product " . Depois, basta me fornecer as linhas que category_id = 124" .
Por ser uma junção esquerda, ela sempre terá as linhas de "catalog_product_entity" . Para qualquer linha que não possa ser correspondida, será NULL
:
Resultado
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
A partir daí, a consulta então diz: "ok, agora me dê tudo onde o category_id é NULL" .