sorting collections by subcategory, an attribute and by productname

走远了吗. 提交于 2019-12-23 06:33:32

问题


I been working on a problem for hours now and it seems I can not find a way to get the above sorting to work.

In an magento project (im relatively new to magento) I have to sort the collection first by subcategory, then an attribute and last by the product name. is Anchor is set to ture with all categories, so parent categories also show subcategory products.

I came across an idea of using the ReflectionObject using the usort() PHP function with an comparator function, like this:

private static function cmp($a, $b) {
    $a_product_geschmack = Mage::getModel('catalog/product')
       ->load($a->getId())->getAttributeText('ws_geschmack');
    $b_product_geschmack = Mage::getModel('catalog/product')
       ->load($b->getId())->getAttributeText('ws_geschmack');

    $r = strcmp(get_category($a), get_category($b));
    if ($r != 0)
       return $r;

    $r = strcmp($a_product_geschmack, $b_product_geschmack);
    if ($r != 0)
       return $r;

    return strcmp($a->getName(), $b->getName());
}

The helper function to get the subcategory looks like this:

function get_category($product) {
    $categoryModel = Mage::getModel( 'catalog/category' );
    // Get Array of Category Id's with Last as First (Reversed)
    $_categories = array_reverse( $product->getCategoryIds() );
    // Get Parent Category Id
    $_parentId = $categoryModel->load($_categories[0])->getParentId();
    // Load Parent Category
    $_category = $categoryModel->load($_parentId);

    return $_category->getName();
}

Using the above comparator with usort and the ReflectionObject in the _getProductCollection() method of Mage_Catalog_Block_Product_List:

// ...
$collection = $this->_productCollection;

$collectionReflection = new ReflectionObject($collection);
$itemsPropertyReflection = $collectionReflection->getProperty('_items');
$itemsPropertyReflection->setAccessible(true); // Make it accessible

$collectionItems = $itemsPropertyReflection->getValue($collection);

usort($collectionItems, array('Mage_Catalog_Block_Product_List', 'cmp'));
$itemsPropertyReflection->setValue($collectionReflection, $collectionItems);
$itemsPropertyReflection->setAccessible(false); // Return restriction back

$this->_productCollection = $collection;
// ...

All above I set up as a test (to see if it is working) in the Mage_Catalog_Block_Product_List class. For savety I commented out the default sorting setting with setOrder in Toolbar.php

Above code I found at https://magento.stackexchange.com/questions/5438/on-search-result-group-products-by-category and it seemed promising (even if this is more a hack than OO).

When I print $collectionItems the order is as expected. In the Frontend it is not as expected, for example the attribute ws_geschmack is not sorted and also the subcategory is not "properly" sorted.

I also ask myself if the way passing back $collection to the _productCollection member is the way to go (as it is in the sample code found in the stackexchange answer). I found out, there is also a method called setValue in the ReflectionObject class, but also does not work.

Well, the problem would be not a real problem, if the subcategory would be an attribute, in this case I could just use the setOrder with an array of fields to sort in ASC order.

Also the order of the categories in the backend (if I order them alphabetically) seem to have no effect. If this would work, I could drop the sorting for the subcategory.

The sorting has to work in the category product listing and in the search results list, the latter is not so important, but the category browsing is.

Another questions is alo similar to mine (https://magento.stackexchange.com/questions/2889/sorting-product-list-by-more-than-one-attribute), but that guy had only attributes and that can be solved with an setOrder Call using an array.

So I am out of ideas. Anybody has an idea how to get this issue solved? Any help is greatly appreciated!

I am using magento version 1.7.0.2 for this project.

Update

Just to clarify what I am looking for: I implemented this iterative code which does exactly what I need. It first gets the sub-categories of the current category, then it queries all the products in these subcategories. The result is sorted list of categories, and the product results / sublists are sorted by the attribute ws_geschmack and name:

$categories = Mage::getModel('catalog/category')->getCollection()
    ->addAttributeToSelect('name')
    ->addFieldToFilter('parent_id',
        array(
            'eq' => Mage::getModel('catalog/layer')->getCurrentCategory()->getId()))
    ->addFieldToFilter('include_in_menu',array('eq' => '1'))
    ->addFieldToFilter('is_active', array('eq' => '1'))
    ->addAttributeToFilter('is_active', 1)
    ->addAttributeToSort('name', 'asc');

foreach($categories as $category) {
    // Check if there are products for sale in this category
    if (Mage::getModel('catalog/category')->load($category->getId())
        ->getProductCollection()
        ->addAttributeToSelect('entity_id')
        ->addAttributeToFilter('status', 1)
        ->addAttributeToFilter('visibility', 4)
        ->count() == 0) continue;


    print "-> " . $category->getId() .': '. $category->getName() . "<br />";

    // Get all child categories below this current category
    $_subcategory_ids = get_categories(Mage::getModel('catalog/category')->getCategories($category->getId()));

    // Build 'finset' query for category_id filter
    $_subcategory_finset_ids = array_map(
        function($elem) {
            return array('finset' => $elem);
        },
    $_subcategory_ids);

    $_products = Mage::getModel('catalog/product')
        ->getCollection()
        ->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id = entity_id', null, 'left')
        ->addAttributeToSelect('*')
        ->addAttributeToFilter('status', 1)
        ->addAttributeToFilter('visibility', 4)
        ->addAttributeToFilter('is_saleable', array('like' => '1'))
        ->addAttributeToFilter('category_id', $_subcategory_finset_ids)
        ->addAttributeToSort('ws_geschmack', 'ASC')
        ->addAttributeToSort('name', 'ASC');

    if ($_products->count() != 0) {
        foreach ($_products as $_product) {
            $prod = Mage::getModel('catalog/product')->load($_product->getId());
            echo $prod->getName() . ": " . $prod->getAttributeText('ws_geschmack') . "<br />";
        }
    }
}

This is just a demo code, I can not use it as is. All that I would need as the return value from getLoadedProductCollection() for example. I guess it will be no easy task to implement that functionality.


回答1:


I'm not sure if this is the expected result, but given your criteria, will the following below set the collection in the way you need it to?

$products = Mage::getModel( 'catalog/product' )
    ->getCollection()
    ->addAttributeToSelect( '*' )
    ->addFieldToFilter( 'status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED )
    ->addFieldToFilter( 'visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH );
    ->addAttributeToFilter( 'category_id', array( 'in' => array( 'finset' => '[CATEGORY_ID_HERE]' ) ) );
    ->addAttributeToSort( 'category_id', ASC )
    ->addAttributeToSort( 'ws_geschmack', ASC )
    ->addAttributeToSort( 'name', ASC );

You can use similar methods to narrow down your collection and simplify things. Also know that if you are customizing the built in product collection methods that the sorter on the front end may be modifying your front-end, so you may want to make sure it's not affecting it.



来源:https://stackoverflow.com/questions/19097839/sorting-collections-by-subcategory-an-attribute-and-by-productname

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!