Reputation: 51
I'm working for a client who owns a Magento-based store. It's chock full of products, but the names of said products are a bit messy. He did not stick to one naming convention but used different conventions throughout the years. So whenever he looks up something using the Name filter in the Admin -> Manage Products section, the results leave a lot to be desired.
So I was wondering if there's a way to make the filtering algorithm a bit more lax, specifically in regards to word order. Because if you have a product with a name like 'word1 word2 word3' and you search 'word1 word3' that products doesn't appear in the results.
Any hint would be appreciated. Cheers!
AUG 28 UPDATE: My search led me to the class *Mage_Adminhtml_Block_Widget_Grid*, and more precisely to its protected method *_addColumnFilterToCollection()*. In here you have the $cond variable, which if you print out it gives you something like this:
Array ([like] => Zend_Db_Expr Object([_expression:protected] => '%search term%' ))
Here if I could intercept that search term and make it into '%search%term%' before it gets submitted to the *Zend_Db_Expr* object I'd probably solve my problem. So, any ideas?
Upvotes: 2
Views: 7402
Reputation: 3057
To do wildcard % search for a given column, and if you can override the core Grid.php
, then you can do something like this in your grid class:
protected function _prepareColumns()
{
parent::_prepareColumns();
// assuming the column is 'name' for product name in the product grid
$this->getColumn('name')->setFilterConditionCallback([$this, 'allowWildcardInFilter']);
...
}
/**
* Allow wildcard % in column search
*/
public function allowWildcardInFilter($collection, $column)
{
if ($value = $column->getFilter()->getValue()) {
if ($field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex()) {
/**
* @var $helper Mage_Core_Model_Resource_Helper_Mysql4
*/
$helper = Mage::getResourceHelper('core');
$likeExpression = $helper->addLikeEscape($value, [
'position' => 'any',
'allow_string_mask' => true
]);
$collection->addFieldToFilter($field, ['like' => $likeExpression]);
}
}
}
Upvotes: 0
Reputation: 2949
You can add 'filter_condition_callback' to any grid in Magento. Create a rewrite for Magento product grid and add this param to 'name' field. Like
$this->addColumn('name',
array(
'header'=> Mage::helper('catalog')->__('Name'),
'index' => 'name',
'filter_condition_callback' => array($this, 'filter_name_callback'),
));
And then determine filter_name_callback method in the same class. Something like this:
protected function filter_name_callback($collection, $column)
{
$names = $column->getFilter()->getValue();
$namesArray = explode(' ', $names);
$cond = array();
foreach ($namesArray as $item)
{
$cond[] = 'main_table.name LIKe %'.$item.'%';
}
$collection->getSelect()->where("(".implode(' OR ', $cond).")");
}
The code was not tested, the example is simple, but I think you understand the idea ;) Good luck!
Upvotes: 2
Reputation: 51
OK, here's a solution for this problem. Please note I have not tested this too much, just on my local test magento. So problems may arise.
Our work class is Mage_Adminhtml_Block_Widget_Grid, found in 'app/code/core/Mage/Adminhtml/Block/Widget/Grid.php'. You should copy this file in 'app/code/local/Mage/Adminhtml/Block/Widget/Grid.php', as to not overwrite core classes.
Now, in that file go to the function named _addColumnFilterToCollection. In here, delete everything inside the '} else {' statement and replace with:
$cond = $column->getFilter()->getCondition();
if ($field == "name" && isset($cond)) {
$filterOrig = $cond['like'];
$filterReplaced = str_replace(" ", "%", $filterOrig);
$newZendDbExpr = new Zend_Db_Expr($filterReplaced);
$modifCond = array('like'=>$newZendDbExpr);
$this->getCollection()->addFieldToFilter($field , $modifCond);
} else if ($field && isset($cond)) {
$this->getCollection()->addFieldToFilter($field , $cond);
}
The '$cond' variable is the key here. If you print it you get something like:
Array([like] => Zend_Db_Expr Object([_expression:protected] => '%filter term%'));
That code snippet basically intercepts the filter term that's passed to the Zend_Db_Expr object, passes it through a str_replace() to replace any whitespace with a '%' wildcard and then sends it back to the object.
So now if you have a product with a name like 'word1 word2 word3' and search filter by term 'word1 word3' you will get proper results. I am open to suggestions, this might not be the best approach. I'll update after properly testing this. Cheers!
SEP 06 UPDATE: Well, after some live testing, results are good and exactly what I wanted when. It seems there is no negative performance impact either.
And the interesting fact is that this little modification applies to filtering in every place where you have a product grid in the admin interface (for example when creating a new order manually and you click Add Products, or in the Manage Categories -> whatever category -> Category Products tab)
SEP 06 UPDATE 2: There is an issue. If you're in the Orders grids and try to filter by status it throws an error. I need to make this filter only apply to the Name field. Any ideas?
SEP 06 UPDATE 3: I revised the script to only apply to the Name field of Product grids. Now there are no conflicts with filter products by visibility or orders by status etc.
Upvotes: 1
Reputation: 12727
That would require coding a custom module having some complexity.
The expectation, that filtering for word1 word3
should at least return a subset of the matches, that filtering by word1 word2 word3
does return, is based on the false assumption that Magento does perform OR
searches for this grid filter.
This is not the case. Magento does not perform OR
, but LIKE
searches in this case.
That also explains, why the result sets of a
LIKE name = '%word1 word2 word3%'
usually never* can match the result set of a
LIKE name = '%word1 word3%'
I think Mage_Adminhtml_Block_Catalog_Product_Grid::_prepareCollection()
will be the minimum that needs to be overridden by your code, to get you started.
Good luck!
* except word2 == word3
, of course
Upvotes: 3