Non-database Smart Search (finder) plugin
Joomla! 2.5 has integrated the Finder-extension into its core, adding a new Smart Search functionality. Already the Joomla! documentation includes a fine tutorial for writing a Smart Search plugin, however it assumes integration with the Joomla! database. With MageBridge, we wrote a SmartSearch plugin that integrates with a remote source (Magento), and we thought it would be a good idea to share this with you all.
Basic functionality of Smart Search
The old Joomla! search relied on plugins that could search the database straight away. The new Smart Search functionality however provides an API for indexing instead: Plugins can still deliver the content, but the Smart Search core determines how this content is indexed and searched. Smart Search plugins can easily be created by extending the class FinderIndexerAdapter (instead of the default plugin-class JPlugin).
The class FinderIndexerAdapter makes it very easy for plugin-developers (see the Joomla! Documentation guide Creating a Smart Search plugin for details): Define your own table ($table variable) and override the getListQuery() method. But this assumes your content is shared in a table inside the Joomla! database. With MageBridge, the bridge (API) is called to query the Magento application remotely via HTTP, and therefor this tutorial did not apply.
Indexing content: getContentCount() and getItems()
So instead of overriding the getListQuery() method, we looked inside the FinderIndexerAdapter class to see which methods actually relied upon this method. Those are 3 methods: getContentCount(), getItems() and getItem(). We focussed on the first two first.
When indexing content, there are 2 ways to fetch the content in the first place: Fetch all the content at once, or fetch them in batches. The Finder-extension facilitates fetching the content in batches. To know how many batches are needed in the first place, you will first need to know how many content-items there are. This is made possible by the getContentCount() method. But instead of making a database-query, you could just override the entire method to call upon your own logic:
protected function getContentCount()
return 10; // replace this with your own logic
In our case, we used this method to use the MageBridge bridge-connection to Magento to fetch the total amount of products. Because there was no API-resource yet for this, we created a new API-resource magebridge_product.count() within the Magento application for this.
The second method that relies on the method getListQuery() is the method getItems(), which of course returns the actual data to be indexed. The return-result should be an array with objects of type FinderIndexerResult.
protected function getItems($offset, $limit, $sql = null)
In our case, we again called upon the Magento API to fetch us a product-collection, and transform this collection into the return-array. Instead of fetching all products, we used the $offset and $limit arguments to only fetch a certain page of the Magento collection. While the $sql argument is required for properly overriding this method, its value is not used in our logic - it's a dummy argument.
In our case, the type_id flag of each $item was not set properly - in the Finder-database it was set to 0. We therefor called upon the getTypeId() method explicitly so that the flag was set:
$item->type_id = $this->getTypeId();
The finder-database also mentions a list_price and a sale_price. We actually don't know what this is for, but - what the heck - we popuated these database-fields anyway with the Magento product-price.
The Joomla! Docs guide also mentions the methods translateState(), getUrl() and getStateQuery() - those are not used in our case either.
In our case, we did not override the method getItem() yet. This method is useful when a specific content-item would be re-indexed, for instance after it was modified in the backend. This would require a plugin-event to first be thrown, after which the finder-plugin could pick up on this event. We skipped this part so far, as it would require a lot of extra work:
The Magento backend would need to catch the Magento event catalog_product_save_after. This would need to be picked up by the MageBridge event-listener to forward this event to Joomla!. In Joomla!, the MageBridge-Magento plugin needs to translate this event into a Joomla! event instead (mageCatalogProductSaveAfter), which can then be picked up by the Finder-plugin to reindex that item. The MageBridge API is completely ready for this, but let's focus first on making sure that SmartSearch gets us what we want, right?
Written by Jisse Reitsma op 15 March 2012