Magento: where are catalog filter's (url get parameter's) handled in magento? and where is sql request made to fetch the result? - magento

Magento currently handles only one filter parameter per category .
I want the filter to handle more than one parameter's for the same category.
eg, Suppose I choose 2 colors red and black in the filter using a checkbox for ex, and then click submit button (which I have added), then I want the page to display results for product's having color red or black.
For this, I want to know :
Where are GET parameter's for any filtering process handled in magento.
Where is the sql request made to fetch the results of filtering process.
So that I could handle more than one filter parameter's and display the results.
Any help or suggestion would be of great help.
Thanks in advance..

For your example scenario, you are filtering using attribute color having its attribute ID. The resource model is responsible to execute the SQL operations being performed on any attribute selection.
Go to file app/code/core/Mage/Catalog/Model/Resource/Layer/Filter/Attribute.php
public function applyFilterToCollection($filter, $value)
{
$collection = $filter->getLayer()->getProductCollection();
$attribute = $filter->getAttributeModel();
$connection = $this->_getReadAdapter();
$tableAlias = $attribute->getAttributeCode() . '_idx';
$conditions = array(
"{$tableAlias}.entity_id = e.entity_id",
$connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
$connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId()),
$connection->quoteInto("{$tableAlias}.value = ?", $value)
);
$collection->getSelect()->join(
array($tableAlias => $this->getMainTable()),
implode(' AND ', $conditions),
array()
);
return $this;
}
In the above function, when you print the collection and exit it before return using,
$collection->printlogquery(true);
you will see the SQL query generated to fetch the results of filtering process.
You can modify this resource model in your local and using INNER JOIN change the SQL query to work for multiple attribute values.

Related

list the values of a product attribute set in magento

how do I find the values of a product's attribute set?
For example, there's a product with an Attribute Set called shirts - T, with attributes of Gender, Shirt Size, and Color. Starting with a $_product object, how do I find the values of the attributes, e.g. Mens, Green, Large?
i am able to getting the attribute set value in the following way:
$product = Mage::getModel('catalog/product')->load($productId);
$prodAttributeSet = Mage::getModel('eav/entity_attribute_set')->load($product->getAttributeSetId())->getAttributeSetName();
I want to get all available attribute set values and codes for specific attribute set(i.e shirt - T)
$_product = Mage::getModel('catalog/product')->load($productId);
Now suppose you want to access value of manufacturer of this product, then consider following code.
$manufacturerValue = $_product->getAttributeText('manufacturer');
As you mention in comment for size and color here i can give you one sample code to use.
If it is a select or multiselect attribute, you still need to map the option ID's to option values. That is the case if you are getting a list of integers instead of human readable labels (e.g. for the color or manufacturer attribute).
// specify the select or multiselect attribute code
$attributeCode = 'color';
// build and filter the product collection
$products = Mage::getResourceModel('catalog/product_collection')
->addAttributeToFilter($attributeCode, array('notnull' => true))
->addAttributeToFilter($attributeCode, array('neq' => ''))
->addAttributeToSelect($attributeCode);
$usedAttributeValues = array_unique($products->getColumnValues($attributeCode));
// ---- this is the different part compared to the previous example ----
// fetch the attribute model
$attributeModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $attributeCode);
// map the option id's to option labels
$usedAttributeValues = $attributeModel->getSource()->getOptionText(
implode(',', $usedAttributeValues)
);
Direct DB query example
Depending on where you want to do this, here is an example of fetching the values without using a product collection. It is slightly more efficient.
Only use the following code in resource models, as thats where DB related code belongs.
This is meant as an educational example to show how to work with Magento's EAV tables.
// specify the attribute code
$attributeCode = 'color';
// get attribute model by attribute code, e.g. 'color'
$attributeModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $attributeCode);
// build select to fetch used attribute value id's
$select = Mage::getSingleton('core/resource')
->getConnection('default_read')->select()
->from($attributeModel->getBackend()->getTable(), 'value')
->where('attribute_id=?', $attributeModel->getId())
->distinct();
// read used values from the db
$usedAttributeValues = Mage::getSingleton('core/resource')
->getConnection('default_read')
->fetchCol($select);
// map used id's to the value labels using the source model
if ($attributeModel->usesSource())
{
$usedAttributeValues = $attributeModel->getSource()->getOptionText(
implode(',', $usedAttributeValues)
);
}
If you want all the attributes of an attribute set you can do the following.
$product = Mage::getModel('catalog/product')->load($productId);
$attributes = $eavConfig->getEntityAttributeCodes( Mage_Catalog_Model_Product::ENTITY, $product );
print_r(attributes);

adding a multiple list attribute to product collection filter in magento

I am trying to do an ajax related magento search, and i can't manage to correctly add a multi-select type product attribute to a product collection,
for instance:
$productModel = Mage::getModel('catalog/product'); //getting product model
$productCollection = $productModel->getCollection();
$productCollection->addAttributeToSelect(
Mage::getSingleton('catalog/config')
->getProductAttributes()
);
$productCollection->addAttributeToFilter(
array(
array('attribute'=>'my_attribute_id',
'finset' => Mage::getResourceModel('catalog/product')
->getAttribute('my_attribute_id')
->getSource()
->getOptionId($searched))
);
where $searched is the string in which i save the keyword. Now, let's presume that my_attribute_id is a multi-select product attribute which has one option named as "Red Bull"...if i search after the exact string "red bull", it works, but i would like to work if i search only after "red" or "bull".
Is there a way to get the option id for an attribute, even if the search string is incomplete? Because the problem is here:
Mage::getResourceModel('catalog/product')
->getAttribute('my_attribute_id')
->getSource()
->getOptionId($searched))
this code returns the id of the attribute option only if i search it entirely.Probably the model does a query something like this
"select...where value='$searched'"
Is there a way to get a list of attribute option id's even if the value of the option is not complete?..so to do a query like this
"select...where value like '%$searched%'"
Or is there a better way to retrieve product collection after a multi-select attribute partial value, other then the solution that I am trying?
Many thanks!
Please try this..
$collection = Mage::getModel('catalog/product')->getCollection();
->addAttributeToSelect('*')
->addFieldToFilter(
'my_attribute_id',
array(
'like' => Mage::getResourceModel('catalog/product')
->getAttribute('my_attribute_id')
->getSource()
->getOptionId($searched)
)
);

Attribute Options/Labels sorting define on Site View Level?

I using Magento 1.6.2.
Is there any way to set the position for Attribute Option Labels on a Site View Level and not on a global level?
Reason: Here the Values for Color in English and German
Black / Schwarz
Clear / Transparent
Copper / Kupfer
Yellow / Gelb
It is obvious that the sorting is different for different languages.
Overriding the position value and sorting the values in the frontpage code is not possible because there are options where alphanumeric sorting doesn't make sense:
i.e. Small Medium Large
Please help
Yes, this is very possible. But it's a fairly deep change, depending on what you would like to accomplish with this. This will get you started:
You want to first add a new column to eav/attribute_option_value table. Here is the setup script for that:
$installer = $this;
$installer->startSetup();
$installer->run("
ALTER TABLE `{$this->getTable('eav/attribute_option_value')}` ADD COLUMN `sort_order` INT UNSIGNED NULL DEFAULT 0;
");
$installer->endSetup();
Next, you need to rewrite Mage_Eav_Model_Mysql4_Entity_Attribute_Option_Collection. When performing the join for the store filter, you need to add your sort_order there:
public function setStoreFilter($storeId=null, $useDefaultValue=true)
{
if (is_null($storeId)) {
$storeId = Mage::app()->getStore()->getId();
}
$sortBy = 'store_default_value';
if ($useDefaultValue) {
$this->getSelect()
->join(array('store_default_value'=>$this->_optionValueTable),
'store_default_value.option_id=main_table.option_id',
array('default_value'=>'value'))
->joinLeft(array('store_value'=>$this->_optionValueTable),
'store_value.option_id=main_table.option_id AND '.$this->getConnection()->quoteInto('store_value.store_id=?', $storeId),
array('store_value'=>'value',
'value' => new Zend_Db_Expr('IF(store_value.value_id>0, store_value.value,store_default_value.value)',
'sort_order'))) // ADDED
->where($this->getConnection()->quoteInto('store_default_value.store_id=?', 0));
}
else {
$sortBy = 'store_value';
$this->getSelect()
->joinLeft(array('store_value'=>$this->_optionValueTable),
'store_value.option_id=main_table.option_id AND '.$this->getConnection()->quoteInto('store_value.store_id=?', $storeId),
'value',
'sort_order') // ADDED
->where($this->getConnection()->quoteInto('store_value.store_id=?', $storeId));
}
$this->setOrder("store_value.sort_order", 'ASC'); // CHANGED
return $this;
}
To show what is going on: each attribute has a source model. The source model is responsible for providing the values in a frontend dropdown-type list (select, multiselect). If the source model is Mage_Eav_Model_Entity_Attribute_Source_Table, which it will be by default if the attribute type is select or multiselect, then this code retrieves the values:
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
->setPositionOrder('asc')
->setAttributeFilter($this->getAttribute()->getId())
->setStoreFilter($this->getAttribute()->getStoreId())
->load();
As you can see, we are rewriting the setStoreFilter() function. This is the last one called. You might want to add an unshiftOrder('main_table.sort_order', 'ASC') at the beginning of the rewritten function, just for good measure that the `main_table.sort_order does not get in the way.
I'll leave it to you to make the necessary adjustments to the admin panel to provide the option for setting the sort order matrix.

Display collection of Shopping cart rules and products categories associated to each rule

I want to check if there is a sales promotion on the product then stick the promotion label on that product on category list page. But I don't know how to loop through all the shopping cart rules and retrieve the products/categories associated to each rule.
EDITED
Thanks seanbreeden, but I can't pull the skus from $conditions. var_dump($conditions); shows this:
{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:12:"category_ids";s:8:"operator";s:2:"==";s:5:"value";s:2:"23";s:18:"is_value_processed";b:0;}}}}}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:2:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_address";s:9:"attribute";s:13:"base_subtotal";s:8:"operator";s:2:">=";s:5:"value";s:2:"45";s:18:"is_value_processed";b:0;}i:1;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:3:"sku";s:8:"operator";s:2:"==";s:5:"value";s:46:"test-config, BLFA0968C-BK001, BLFA0968C-CR033X";s:18:"is_value_processed";b:0;}}}}}a:6:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:6:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:3:"sku";s:8:"operator";s:2:"==";s:5:"value";s:16:"BLFA0968C-CR033X";s:18:"is_value_processed";b:0;}}}}}
but when I loop through $conditions i.e.
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
$conditions = $rule->getConditionsSerialized();
foreach ($conditions as $condition) {
var_dump($condition);
}
}
it doesn't show anything so don't really know how to pull skus here.
EDIT2
As Alaxandre suggested, I'm not using unserialized approach. I'm doing it like this now:
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
if ($rule->getIsActive()) {
//print_r($rule->getData());
$rule = Mage::getModel('salesrule/rule')->load($rule->getId());
$conditions = $rule->getConditions();
$conditions = $rule->getConditions()->asArray();
foreach( $conditions['conditions'] as $_conditions ):
foreach( $_conditions['conditions'] as $_condition ):
$string = explode(',', $_condition['value']);
for ($i=0; $i<count($string); $i++) {
$skus[] = trim($string[$i]);
}
endforeach;
endforeach;
}
}
return $skus;
And then checking in list page if sku matches within $skus array then show the label. But again there are limitation with this approach as well. I'm think of another approach (I'm not sure if thats is possible).
Thinking of creating a new table (to save the sales rules products).Everytime save the sales rule, catch the save rule event and update the table with Rule name and all the associated products. Then on the list page check that table, if products exist in the table, show the appropriate label. Now I think the event is adminhtml_controller_salesrule_prepare_save (not 100% sure) but I don't know how to get the sku from the rule condition in the observer to save in the new table.
I would suggest you to do it like this. When you had a product to cart, each rules are checked to calculate the final price and reduction. You can know which rules are applied to each item of your cart. In the table sales_flat_quote_item you have the column applied_rule_ids. I think you can access to this in php, by a function getAllItemsInCart or something like this (you have to find out). After you do $item->getAppliedRuleIds() and finally you can get the name of the rule apply to an item (product).
Good luck :)
Edit:
I read again your request and I think my answer doesn't fit with your request.
Your case is even more complicated. For each product on your catalog page you have to apply all the rules of your website. But Mage_SalesRule_Model_Validator process expect item and not product...
If you have lot of rules this task will slow down your catalog and this is really not good! The best would be to cache this result of the rules label in the database, may be in the table catalog_category_product or... (and even better to generate this cache automatically).
Edit2:
Other possibility would be to have a new field in rule creation where you set manually the related products (sku). You save this data in the table salesrule or in a new table salesrule_related_sku.
Then when you display the catalog you check for the sku and if the rule still active.
This solution would be the easiest one :-)
You could pull the getMatchingProductsIds from /app/code/core/Mage/CatalogRule/Model/Rule.php and compare them with the skus displayed on the category list page.
$catalog_rule = Mage::getModel('catalogrule/rule')->load(1); // ID of your catalog rule here, or you could leave off ->load(1) and iterate through ->getCollection() instead
$catalog_rule_skus = $catalog_rule->getMatchingProductIds();
hth
EDIT
Here's a way to get the serialized conditions:
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
$conditions = $rule->getConditionsSerialized();
var_dump($conditions);
}
EDIT 2
There would have to be a better way to do this. The only way I could pull that data was to unserialize then iterate with foreach through each layer. Anyone have any better ideas for this? This works but is very sloppy.
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
if ($rule->getIsActive()) {
$conditions = $rule->getConditionsSerialized();
$unserialized_conditions = unserialize($conditions);
$unserialized_conditions_compact = array();
foreach($unserialized_conditions as $key => $value) {
$unserialized_conditions_compact[] = compact('key', 'value');
}
for ($i=0;$i<count($unserialized_conditions_compact);$i++) {
if (in_array("conditions",$unserialized_conditions_compact[$i])) {
foreach($unserialized_conditions_compact[$i] as $key => $value) {
foreach($value as $key1 => $value1) {
foreach($value1 as $key2 => $value2) {
foreach($value2 as $key3 => $value3) {
$skus[] = explode(",",$value3['value']);
}
}
}
}
}
}
}
}
var_dump($skus);
The rules are associated to all product for a website. There is no rules set for a specific products/categories from the database point of view. For each product in the cart, Magento will validate all the rules you have for a website. This operation is done in the class Mage_SalesRule_Model_Validator. The only way to solve your request is to extend the function process from this class (at least I think so :p).
I wanted the same thing as you want. I wanted to get associated SKUS, Category Ids and any other conditions value to generate Google feeds to be used in Google merchant promotions.
I have used the recursive function to reach to last children of the condition and fetch its value.
I am checking based on the attribute value of the condition. If an attribute value is blank then go one step down and check if attribute value present and if so then fetch the value of it otherwise continue to go down.
Here is the code that I used to fetch values. Which will also work for the case, when two conditions are on the same level.
public function get_value_recursively($value){
foreach($value as $key => $new_value) {
if(strlen($new_value[attribute]) == 0){
$value = $new_value[conditions];
return $this->get_value_recursively($value);
}else{
$resultSet = array();
if (count($value) > 1){
for ($i=0;$i<count($value);$i++) {
$resultSet[] = array('attribute' => $value[$i][attribute], 'value' => $value[$i][value]);
}
$result = $resultSet;
}else{
$result = array('attribute' => $new_value[attribute], 'value' => $new_value[value]);
}
return json_encode($result, JSON_FORCE_OBJECT);
}
}
}
according to #seanbreeden answer you can call this function from first foreach
It will return the result like this :
{"0":{"attribute":"category_ids","value":"5, 15"},"1":{"attribute":"sku","value":"msj000, msj001, msj002"}}
P.S. I am not PHP dev. So, Ignore layman style code. :)

Magento custom options grid

I have an extension for - Advanced custom options.
I would like to display the related options for every products in the order/grid.
This is the code I use:
protected function _prepareCollection()
{
$collection = Mage::getResourceModel('sales/order_grid_collection');
$collection->getSelect()->join
(
'sales_flat_order_item',
'sales_flat_order_item.order_id = main_table.entity_id',
array
(
'price' => new Zend_Db_Expr('group_concat(sales_flat_order_item.price SEPARATOR "<br><br><hr>")'),
'proname' => new Zend_Db_Expr('group_concat(sales_flat_order_item.name SEPARATOR "<br><hr>")'),
'proptions' => new Zend_Db_Expr('group_concat(sales_flat_order_item.product_options SEPARATOR "<br><hr>")'),
)
);
$collection->getSelect()->group('main_table.entity_id');
$this->setCollection($collection);
return Mage_Adminhtml_Block_Widget_Grid::_prepareCollection();
}
The result I get is incomplete array of the product option related to the product.
How can I get the (proptions) lables/title?
Can you add the following line above your return statement and give me the output? I have a feeling if you're getting a partial response, it may be related to the SQL query itself.
Mage::log($collection->getSelect()->__toString());
You might also try giving leftJoin a shot instead of an inner join by using $collection->getSelect()->leftJoin(), depending on the structure of your query.

Resources