问题
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