He encontrado una forma algo mejor de hacerlo, usando un anti-join (Magento 1.9).
Beneficios de este enfoque
El beneficio de esto sobre la respuesta original es que no obtendrá falsos positivos, y como resultado es más rápido y menos propenso a errores. Por ejemplo, suponga que tiene un solo producto:
- Camisa (en categorías: 1 | 2)
Desea "encontrar todos los productos que no están en él category 3
y luego agregarlos a category 3
" . Entonces ejecuta una NOT IN
consulta, y devolverá dos filas (name | category_id)
:
1. "Shirt" | 1
2. "Shirt" | 2
No es gran cosa, Magento solo devolverá el primer resultado y luego lo agregará. Excepto ! La segunda vez que se ejecuta esta consulta, tiene los mismos resultados:
1. "Shirt" | 1
2. "Shirt" | 2
Y Magento te dirá que todavía no le has agregado esta camisa category 3
. Esto se debe a que cuando un producto pertenece a varias categorías, tendrá varias filas en la tabla "catalog_product_entity" . Y entonces a LEFT JOIN
devolverá múltiples resultados.
Esto no es deseable porque
- Su conjunto de resultados será más grande de lo necesario, lo que utilizará más memoria de la necesaria. Especialmente si tienes un inventario muy grande de miles de artículos.
- Tendrá que hacer una verificación adicional en PHP para determinar si los resultados son falsos positivos (por ejemplo,
in_array($categoryThree, $product->getCategories())
), lo que significa que pasará a través de resultados innecesarios. Esto hará que su script / código sea más lento, especialmente con grandes inventarios.
Solución
// 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 consulta SQL generada se verá así:
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`;
Explicación:
Dadas las tablas de relación producto y producto <=> categoría:
catalog_product_entity
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
catalog_category_product
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
Su consulta dice: " deme todas las filas en " catalog_product_entity " y péguelo en la columna" category_id "de " catalog_category_product " . Luego, solo deme las filas que category_id = 124" .
Debido a que es una combinación izquierda, siempre tendrá las filas de "catalog_product_entity" . Para cualquier fila que no pueda coincidir, será NULL
:
Resultado
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
A partir de ahí, la consulta dice: "ok, ahora dame todo donde el category_id es NULL" .