I'm trying to change the sorting of my products.
I want to use the attribute sort by options not to sort by name but rather sort by position given in the "manage attributes". But only when there's a position (when all is 0 it should sort by name).
For this I need to know in which file the sorting is applied to the product collection. I searched inside app\code\core\Mage\Catalog\Model\Config.php and app\code\core\Mage\Catalog\Block\Product\List.php but neither found anything about the sorting. And maybe some code suggestions, too.
Google isn't helping me with this issue, too so I try it here. Maybe somone can help me with this.
Thanks
EDIT:
As example we take the "Color" attribute, it's a drop-down attribute.
The table inside "manage attributes" looks like this:
Valuename | Position
========================
light blue | 1
blue | 2
dark blue | 3
light red | 4
red | 5
dark red | 6
In the frontend I sort by the attribute "Color" and my products will be listed this way:
blue
dark blue
dark red
light blue
light red
red
But I want it to be listed like this:
light blue
blue
dark blue
light red
red
dark red
EDIT2:
public function getAllOptions($withEmpty = true, $defaultValues = false)
{
$storeId = $this->getAttribute()->getStoreId();
if (!is_array($this->_options)) {
$this->_options = array();
}
if (!is_array($this->_optionsDefault)) {
$this->_optionsDefault = array();
}
if (!isset($this->_options[$storeId])) {
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
->setPositionOrder('asc')
->setAttributeFilter($this->getAttribute()->getId())
->setStoreFilter($this->getAttribute()->getStoreId())
->load();
$this->_options[$storeId] = $collection->toOptionArray();
$this->_optionsDefault[$storeId] = $collection->toOptionArray('default_value');
}
$options = ($defaultValues ? $this->_optionsDefault[$storeId] : $this->_options[$storeId]);
if ($withEmpty) {
array_unshift($options, array('label' => '', 'value' => ''));
}
return $options;
}
This is almost impossible to achieve if you want to go with the actual color attribute.
The product collection can get the text value of each color from the EAV structure and run a sorting based on it. But the attribute option table isn't joined to the collection, so there's no way it could know what position you've set for your options.
I would make an workaround for that, maybe not the best overall solution, but you'll get what you want.
Create an attribute with code smth like sorting_color and label Color, and numerical options (e.g. 1,2,3). And assign proper values to each product (so "light blue" would be 1, "blue" is 2 - and so on). Then remove the actual color attribute from sorting, and add your newly created sorting_color.
As I said not the exact solution, but you could get yourself into troubles if you choose to mess up with the product collection sorting.
I was also facing the same problem and solved it by using some custom code:
I want to share here so that someone will get idea from this:
First I got all attributes options of color sorted by their position
$attribute = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'color');
if ($attribute->usesSource())
{
$options = $attribute->getSource()->getAllOptions('positions');
}
foreach($options as $val)
{
$color_attr_array[] = $val['label'];
}
I obtained all the attribute options of color sorted by their position
foreach($childProducts as $_childProduct)
{
//get the attribute option of a particular product and find itd key from the attributes array
$color_attr_val = $_childProduct->getAttributeText('color');
$color_pos = array_search($color_attr_val,$color_attr_array);
//make a new array with key indexes of attribute options
$_childproduct_final_arr[$color_pos] = $_childProduct;
}
ksort($_childproduct_final_arr);
//This is the final array of products sorted by attribute options position
I found the function addValueSortToCollection in Mage_Eav_Model_Entity_Attribute_Source_Table is what defines the sort order. By adding a LEFT JOIN to the table eav_attribute_option and pulling in the sort_order field I was able to sort by the adminhtml position of the attribute. I haven't tested fully but it seems to do what I want.
I created a separate module to rewrite this class, the only modification is to addValueSortToCollection, below this line:
Mage::getResourceModel('eav/entity_attribute_option')
->addOptionValueToCollection($collection, $this->getAttribute(), $valueExpr);
I added the following Join, and modified the getSelect statement to sort by position 1st, then alphabetical second:
// add join here to pull in sort_order for attribute
$collection->getSelect()->joinLeft(
array($this->getAttribute()->getAttributeCode() . '_option' => 'eav_attribute_option'), "{$this->getAttribute()->getAttributeCode()}_option_value_t1.option_id=" . $this->getAttribute()->getAttributeCode() . "_option.option_id"
);
$collection->getSelect()
->order("sort_order {$dir}") // Add sort_order to the select statement
->order("{$this->getAttribute()->getAttributeCode()} {$dir}");
return $this;
}
Hope someone finds this helpful.
de
Just open this file Mage/Eav/Model/Entity/Attribute/Source/table.php and there is one function is there getAllOptions() in which try this
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
->setPositionOrder('asc')
->setAttributeFilter($this->getAttribute()->getId())
->setStoreFilter($this->getAttribute()->getStoreId())
->load();
OR
please try to see weather there is comment or not this in app/code/core/Mage/Catalog/Model/Config.php:
public function getAttributeUsedForSortByArray()
{
$options = array(
**‘position’ => Mage::helper(‘catalog’)->__(‘Position’)**
);
foreach ($this->getAttributesUsedForSortBy() as $attribute) {
/* #var $attribute Mage_Eav_Model_Entity_Attribute_Abstract */
$options[$attribute->getAttributeCode()] = $attribute->getStoreLabel();
}
return $options;
}
may this will help you
let me know if you still facing any problem
Related
I have a SESSION['cart'] with ID numbers only. I have a form passing the ID with a remove button. Once passed to my controller, I cannot figure out how to write the code that uses the ID ($_POST['id']) to delete the item from the SESSION['cart'].
I can loop through and display the array contents, but I cannot figure out how to delete based on ID passed from the form.
How do I loop through the SESSION['cart'] array to find a match with the ID passed from my delete form, and then delete that ID? I know that unset($_SESSION['cart'][X] deletes the ID at index X, but I cannot figure out how to loop through all the elements to find a match.
I have read a number of related issues in this forum but have been unable to apply any of those solutions to resolve this challenge. Any assistance is appreciated.
The way you have your values ($products = array(3,7,99,152)) isn't a very good method. Every time you want to perform an action, you have to loop through the array, you don't want that. Apart from that, how do you store quantity? Or variations like e.g. size or color?
if your structure is $array[ ID_OF_PRODUCT ], you can simply do this:
unset( $_SESSION['cart'][$_POST['id']] ); // Instant access via the key!
This should be the method to use. This allows you to create an array like this, with advanced info, but with easy access (42/63 are example id's)
$_SESSION['cart']['products'][42] = array(
'quantity' = 11,
'size' = 'large',
'color' = 'blue'
);
$_SESSION['cart']['products'][63] = array(
'quantity' = 9,
'size' = 'small',
'color' = 'red'
);
This way you can access a lot of info with the product ID, and now also see which size and color (both just examples) the user selected. You may not have need for this now, but you will further down the road :)
As you might see, you can easily do stuff with the item:
isset($_SESSION['cart'][$_POST['id']]); // check if the product exists
unset($_SESSION['cart'][$_POST['id']]); // remove the product
echo $_SESSION['cart'][$_POST['id']]['quantity']; // get the quantity.
Not a loop in the code. You should only use loops when you have to, try to somewhat avoid them because often their slow. Say you have an extreme case of 1000 items in your shop, and you need to delete no999... That'll take a noticable moment.
Here is the code to do it right:
$id = $_POST['id'];
$items = $_SESSION["cart"];
if(($key = array_search($id, $items)) !== false) {
unset($items[$key]);
}
$_SESSION["cart"] = array_values($items);
Advice
Beside item ID, you can also sve item count in SESSION array because user can add several times same item into cart. In that case your $_SESSION["card"] should be structured like:
array(
'1'=>12,//Item with ID=1 is added 12 times in shopping cart
'17'=>2,//Item with ID=17 is added 2 times in shopping cart etc.
'32'=>12,
)
I need to generate a table of filterable attributes with values of current product, something like
color - 3
size - 5
x - y
...
In my view.phtml
Problem is: my shop will have many attribute sets, and attributes can change. So I can't get attributes value by name.
I get an attribute sets code of current product, and retrieve all attributes of this set. But I don't know how filtered it by only filterable attributes?
Or maybe someone know simpler way to do this?
Thank you, and sorry for my English
Roman, you can get all filterable attribute by below code. Modify/add your condition to get the specific data accordingly.
$collection = Mage::getResourceModel('catalog/product_attribute_collection');
$collection
->setItemObjectClass('catalog/resource_eav_attribute')
->setOrder('position', 'ASC');
$collection->addIsFilterableFilter();
$result = array();
foreach ($collection as $attribute) {
$result[] = array('value' => $attribute->getAttributeCode(), 'label'=>$attribute->getFrontendLabel());
}
I'm looking for the best way to create a complex navigation element on the fly. I have all of the elements in a database (title, id, parentId) and I want to efficiently take them out of the DB and display them correctly. I also want to collapse all of the navigation elements that aren't active. So if I was browsing through "Sofas" I wouldn't see "Chandeliers" or any of the categories under lighting but I would see "Lighting".
This is what I want the final product to look like:
Furniture
Living Room
Sofas
Chairs
Ottomans
Bedroom
Beds
Nightstands
Lighting
Chandeliers
Floor Lamps
Sconces
Rugs & Textiles
Contemporary
Vintage
My current method is
write one SQL query that pulls down all of the category names, ids, and parent ids
Iterate through the categories and put into a sorted multi-dimensional array with child categories stored under their parents.
Iterate through the new array and add another entry to mark the appropriate categories as open (all categories are closed by default)
iterate through the array and write HTML
I'm trying to to this with as few interations as possible and I'm sure the code I have right now is inefficient. Especially step 2 I iterate through the array several times. There has to be a general solution to this (common?) problem.
Consider adding a new field to your database table: level.
Main-categories will have level 0.
Sub-categories will have level 1.
Sub-sub-categories will have level 2.
etc.
This trick will help you to know which sub-categories to disable without 2nd iteration of the array.
I believe this is the perfect place to generate your html code using recursion.
I used this function a while ago. It is working with a multi-dimensional array (tree)
function buildMenu($menu_array, $is_sub=FALSE) {
$attr = (!$is_sub) ? 'id="menu"' : 'class="submenu"';
$menu = "<ul $attr>\n";
foreach($menu_array as $id => $elements) {
foreach($elements as $key => $val) {
if(is_array($val)) {
$sub = buildMenu($val, TRUE);
}
else {
$sub = NULL;
$$key = $val;
}
}
if(!isset($url)) {
$url = $id;
}
$menu .= "<li>$display$sub</li>\n";
unset($url, $display, $sub);
}
return $menu . "</ul>\n";
}
echo buildMenu($menu_array);
This adds css properties too. If you wish to mark the currently active page you can use the strpos() function to find your current url. If you need some more functionality you can easily add them to buildMenu()
Using level as mentioned in the answer above will help too. If you were using the nested set model in your database I could also help you with my query which is a single select returning the whole menu data.
I need help to traverse all options in a multioption.
I use the Product-class with a new multioption-attribute called "product_properties". I need a function to check if the optionID the user chose on the front-end matches an option in the list, and return true if a match is found.
This way I can check if e.g. the user chose "Red" as the "Color" on a product.
In pseudo-code this is what I need:
Parameters: postedOptionID, currentObjectID
Fetch attribute "product_properties" (multioption) on object .
For each option for "Color" in "product_properties"
2.1 If postedOptionID == optionID
2.1.1 return true
Thanks
I finally found a way :)
$product_properties_name is the name of a class-attribute that's an 'ezmultioption'-datatype. In my case it's called 'product_properties' and is an attribute on the 'Product'-class.
First get all of the object's attribute:
$contentObjectAttributes = $contentObject->version($contentObject->attribute( 'current_version' ) )->contentObjectAttributes();
and then loop each and find 'product_properties':
// Loop all attributes of the object's class
foreach(array_keys($contentObjectAttributes) as $key)
{
$contentObjectAttribute = $contentObjectAttributes[$key];
$contentClassAttribute = $contentObjectAttribute->contentClassAttribute();
$attributeIdentifier = $contentClassAttribute->attribute("identifier");
// Get 'product_properties'-attribute
if ($attributeIdentifier == $product_properties_name)
{
// Get the multioption
$multioption_list = $contentObjectAttribute->content();
// Loop all multioption lists (Color, Make, Brand etc.)
foreach($multioption_list->attribute('multioption_list') as $index => $option)
{
// Loop through this multioption and get all options (if 'Color', get 'Blue', 'Red', 'Green' etc.)
foreach($option['optionlist'] as $option)
{
$optionValue = trim($option['value']);
// if there's a match on $optionValue, do something interesting...
}
}
}
}
Hi!
I didn't get any code that worked. Of course I could have use then wrongly because I'm a beginner. Some told me to use MySQL subqueries other told me to use PHP foreach achieve it.
What I want is to show the search results of a keyword separated by groups of categories, something like that:
Search results for Item, 3 itens in 2 categories:
Category 1:
Item 1
Item 10
Category 2:
Item 1003
Can someone explain me it as simple as possible.
Thanks n advance!
I use a single request which return name of category for each item and I use PHP to display it
<?php
$cat;
while($result = $statement->fetch()) {
if($result['cat'] !== $cat) {
$cat = $result['cat'];
/* display cat */
}
/* display items */
}
?>