dragan atanasov

// blog about developer thoughts

Modifying Magento search

Dec 05, 2013magento

The problem:

Search in Magento Searching for something…

Searching for something sometimes can be good, sometimes can be bad. As you can see on the image above, search autocomplete shows you search suggestions based on what your customers previously searched. That is good thing though, but if your site is still new, autocomplete will show all results that are matching previously searched terms, some of them will be relevant to some product and some will have nothing in common with your products or site but some users typed them in search and even some words that you don’t want to see on your site.

\– Search terms can be managed from Administration panel (Catalog->Search Terms) and administrator can add, edit or remove search terms to make better search suggestions.*

The solution (one of many):

To improve search suggestions we need to change the way search gets the results. Luckily Magento has model rewriting system, so we can save time and don’t need to reinvent the wheel. First thing to do is to find the model that loads the collection with results before passing them to controller. In this case that model class is MageCatalogSearchModel_Query and as the name suggests we can find this file in app/code/core/Mage/CatalogSearch/Model/Query.php and let’s examine the file. We are interested in getSuggestCollection() method:

/**
 * Retrieve collection of suggest queries
 *
 * @return Mage_CatalogSearch_Model_Resource_Query_Collection
 */
public function getSuggestCollection()
{
    $collection = $this->getData('suggest_collection');
    if (is_null($collection)) {
        $collection = Mage::getResourceModel('catalogsearch/query_collection')
            ->setStoreId($this->getStoreId())
            ->setQueryFilter($this->getQueryText());
        $this->setData('suggest_collection', $collection);
    }
    return $collection;
}

Here we see that Magento gets search result collection from suggestion collection and we need to change that.

In order not to change core files we need to rewrite this method. To do this first we need to create module under our namespace. When rewriting core modules, it’s often good to give your module same name as the module that you are rewriting so later you or other developer can easily find this module. The core module can be found in core/Mage/CatalogSearch so we create our module in local/Themodule/CatalogSearch and because we are rewriting Mage_CatalogSearch module we need to add dependency to our module activation file, in this case : app/etc/modules/Themodule_CatalogSearch.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Themodule_CatalogSearch>
            <codePool>local</codePool>
            <active>true</active>
            <depends> <!-- Dependencies -->
                <Mage_CatalogSearch />
            </depends>
        </Themodule_CatalogSearch>
    </modules>
</config>

Example of module activation file

Now, let’s write module configuration XML. To tell Magento that we want to rewrite some model, we need to add following in config.xml of our module:

<config>
    ..
    ..
    <global>
        ..
        ..
        <models>
            <catalogsearch>
                <rewrite>
                    <query>Themodule_CatalogSearch_Model_CatalogSearch_Query</query>
                </rewrite>
            </catalogsearch>
        </models>
    </global>
    ..
    ..
</config>

If you are beginner in Magento XML configurations can be very hard to understand, but it is quite simple. Here we are telling Magento that we want to rewrite Query model from CatalogSearch module and inside node we put our class that we want to rewrite. Our model class will be ThemoduleCatalogSearchModelCatalogSearchQuery and we create Query.php file in Model/CatalogSearch/ folder.

class Themodule_CatalogSearch_Model_CatalogSearch_Query extends Mage_CatalogSearch_Model_Query
{
}

Initial content of our Query.php

You notice from the code above that our model extends Mage > Core model because we need to override getSuggestCollection() method. If we want our search suggestions to be based on product names, this method should look like this:

public function getSuggestCollection()
{
    $collection = $this->getData('suggest_collection');
    if (is_null($collection)) {
        $collection = Mage::getModel('catalog/product');
        Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection); //Filter disabled products
        $collection->getCollection()            ->addAttributeToSelect('name')
            ->addAttributeToFilter('name', array('like' => '%'.$this->getQueryText().'%'))
            ->addAttributeToSort('name', 'ASC')
            ->setPageSize(10)
            ->addStoreFilter($this->getStoreId())
        ;
        /*
        * Filter out of disable, out of stock and invisible products
        *
        *
        */
        Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
        Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection);
        Mage::getSingleton('catalog/product_visibility')->addVisibleInSearchFilterToCollection($collection);
        $this->setData('suggest_collection', $collection);
    }
    return $collection;
}

Using this code we get collection with products that have product names that contain query string that customer has entered in search. Additionally I applied soring by name and results are limited to 10. Of course you can change filtering and sorting to your liking or even create own custom settings in Administration and allow administrator to manage results for search suggestions. If you save the file and try to search you will get an error. We got this error because we modified collection and now the block that is responsible for generating HTML can’t find required variable. To fix this issue we need to find where this block is being generated. Open app/code/core/Mage/CatalogSearch/Block/Autocomplete.php and take look at getSuggestData() method. On this line of code:

php$item->getQueryText()

We see that Autocomplete searches for “query_text” filed. This field does not exists in our modified collection. Now there are two possible solutions for this problem:

  1. Rewrite MageCatalogSearchBlock_Autocomplete::getSuggestData() method and this will allow us to make more customizations, or
  2. We can simply create alias field “query_text” in our collection and leave default Autocomplete block rendering.

First solution is very similar with model rewriting and as exercise you can try and solve for yourself :) . Second solution is more interesting for me and hope for you too. I’m guessing you all know how to create field alias in SQL, in Magento is also easy and simple. We can do this with addExpressionAttributeToSelect($alias, $expression, $attribute) method. In our case that will this will look something like this:

$collection->addExpressionAttributeToSelect('query_text', '{{name}}', 'name');
public function getSuggestCollection()
{
    $collection = $this->getData('suggest_collection');
    if (is_null($collection)) {
        $collection = Mage::getModel('catalog/product');
        Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection); //Filter disabled products
        $collection->getCollection()            ->addAttributeToSelect('name')
            ->addAttributeToFilter('name', array('like' => '%'.$this->getQueryText().'%'))
            ->addExpressionAttributeToSelect('query_text', '{{name}}', 'name')
            ->addAttributeToSort('name', 'ASC')
            ->setPageSize(10)
            ->addStoreFilter($this->getStoreId())
        ;
        $this->setData('suggest_collection', $collection);
    }
    return $collection;
}

Updated getSuggestCollection() method with alias.

Now if we save the file and try to search on the site we should get this: Enhanced search in Magento Now we are getting search suggestions from real product names.

If click on some search suggestion you will be taken to search results with that query string.