카테고리에없는 제품을 필터링하는 방법은 무엇입니까?


10

내 코드는 다음과 같습니다.

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

카테고리 ID 목록에없는 모든 제품을 가져오고 싶지만 코드에서 예상 결과를 얻지 못했습니다. 고마워 보여주세요.


기대했던 결과와 얻은 결과는 무엇입니까?
ahnbizcad

답변:


16

카테고리 / 제품 관계가있는 테이블을 조인해야합니다.

범주 목록에서 모든 제품을 찾기 위해 사용하는 컬렉션의 변형은 다음과 같은 트릭을 수행해야합니다.

(추천되었지만 올바른 길로 안내해야 함)

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

심판 : http://www.proxiblue.com.au/blog/Collection_of_products_in_all_child_categories/


1
제품이 둘 이상의 범주에 있으면 작동하지 않는 것 같습니다. 'nin'은 해당 행을 제외하지만 제품 ID는 다른 범주에 계속 나타납니다.
greatwitenorth

안녕하세요, 코드가 서서 100 % 작동합니다. 작성된 코드는 변수 $ catIds의 지정된 카테고리 목록에 따라 제품을 제외시킵니다. 제품이 둘 이상의 범주에 나타나고 해당 범주가 제외 목록에 포함되지 않은 경우 해당 제품이 나타납니다. 그러면 모든 카테고리를 배제하지는 않습니다. 코드는 마술처럼 (작성된대로) 제품에 어떤 다른 카테고리가 나타나는지 알 수 없습니다.
ProxiBlue

제품 ID로 제외하는 방법을 원 하듯이 위의 코드에서 쉽게 파생시킬 수 있습니다. 첫 번째 방법은 IN으로 바꾸고이 쿼리에서 결과로 얻은 제품 ID를 가져온 다음 id로 주어진 제품을 제외하고 제품의 새 컬렉션에서이를 사용하는 것입니다. 제품 ID로 제외되는 제품 콜렉션의 하위 쿼리로 만드는 것이 더 좋습니다.
ProxiBlue

5

다음 코드가 도움이 될 것입니다.

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

1

안티 조인 (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

그리고 마 젠토는이 셔츠를 아직 추가하지 않았다고 알려줄 것입니다 category 3. 제품이 여러 범주에 속하는 경우 "catalog_product_entity" 테이블 에 여러 행이 있기 때문 입니다. 따라서 a 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 | +-------------+-------------+

그런 다음 쿼리는 "ok, 이제 category_id가 NULL 인 모든 것을 제공합니다" 라고 말합니다 .


1

보기에는 쉽지 않습니다.

GROUP_CONCAT 기반 옵션은 기본 범주 (1024이지만 물론 증가 할 수 있음)이므로 쉼표로 구분 된 제품 범주 ID를 사용하는 것이 좋습니다.

$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이 마음에 들지 않으면) 제품 ID의 하위 쿼리가 실제로 IN 카테고리에있는 카테고리에 WHERE product_id를 사용할 수 있습니다 (여기서는 제공하지 않음).

다른 답변의 반 조인 방식 도 효과가 있습니다. 그러나이 경우 추가 조건을 쉽게 추가 할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.