Writing your own Finder plugin - Part 1
DRAFT - WORK IN PROGRESS
This is part 1 in a series that will outline how to design your own search plugin for Finder. In this tutorial we look at building the indexer for a particular type of content.
Please note that this information is provided for informational purposes only. Support for creating new Finder plugins is not covered under regular support subscription. Additional fees for assistance can be negotiated on a case-by-case basis.
You must have a good working of PHP and Joomla extension development to attempt this tutorial.
Setting the Scene
This example will be based on a real-life project for integrating information from software called Stone Orchard into Joomla. Stone Orchard is a SQL Server based application for managing cemetery burials. Joomla was chosen to act as a web display mechanism for the project which involved caching a table of information in MySQL. It is this table that we will be wanting Finder to index.
The completed source code for this plugin and be found in the GraveFinder project at JoomlaCode.org. Note that this is the live instance and the code may differ from the examples given in the rest of this tutorial.
Creating the Plugin
The first step in the process is to create the shell for the Joomla plugin. We need to create two files in the Finder plugin folder:
/plugins/finder/stone_burials.php
/plugins/finder/stone_burials.xml
and a translation file:
/administrator/language/en-GB/en-GB.plg_finder_stone_burials.php
The translation file is used to store text that is used in the metadata file.
The Plugin Metadata File
At this stage of development, the metadata file stone_burials.xml is reasonably simple.
<?xml version="1.0" encoding="utf-8"?> <install version="1.5.14" type="plugin" group="finder"> <name>Finder - Stone Orchard Burials</name> <group>finder</group> <version>1.0.0</version> <creationDate>25 August 2009</creationDate> <author>Andrew Eddie</author> <authorEmail> This e-mail address is being protected from spambots. You need JavaScript enabled to view it </authorEmail> <authorUrl>http://www.newlifeinit.com</authorUrl> <copyright>2009 New Life in IT Pty Ltd. All rights reserved.</copyright> <description>Stone_Burials_Finder_Plugin_Desc</description> <files> <filename plugin="stone_burials">stone_burials.php</filename> </files> </install>
This is a very typical metadata file for a Joomla plugin. No parameters are needed yet.
The Main Plugin File
For now, we will just construct a shell.
<?php
/**
* @version $Id: catalog_nodes.php 357 2009-01-29 11:02:04Z robs $
* @copyright Copyright (C) 2009 New Life in IT Pty Ltd. All rights reserved.
* @license GNU General Public License version 2 or later
* @link http://www.newlifeinit.com
*/
defined('JPATH_BASE') or die;
require_once dirname(__FILE__).DS.'_prototype.php';
$lang = &JFactory::getLanguage();
$lang->load('plg_finder_stone_burials');
/**
* Finder adapter for Stone Orchard (for Joomla) burial information.
*
* @package NewLife.Stone
* @subpackage plg_finder_stone_burials
*/
class plgFinderStone_Burials extends plgFinderPrototype
{
/**
* @var string The data table for the items to be indexed
*/
var $_src_table = '#__stone_burials';
/**
* @var string An abitrary but unique context for this plugin
*/
var $_context = 'Stone_Burials';
/**
* @var string The name of the item being indexed. Found under the "Type" Content Map
*/
var $_type_title = 'Burial';
/**
* @var string The alias of the item being indexed. Found under the "Type" Content Map
*/
var $_type_alias = 'burial';
}
Following the standard prologue for a Joomla file (the DocBlock and the direct access preventive), we load a plugin prototype upon which we will base our plugin class. The language file for the plugin is also loaded by hand as this is not done automatically by the Joomla framework.
The plugin class must follow the standard naming convension for Joomla plugins.
Now we need to define four variable that integrate with Finder.
- _src_table - This is the name of the table that holds the source data that we are going to index.
- _context - This is a unique label that Finder uses internally so that the information this plugin uses doesn't get confused with any other Finder plugin.
- _type_title - This is the title of the type of content that is being indexed. This gets added under the Type content map branch.
- _type_alias - This is the alias for the type of content being indexed (it's simply the SEF representation of the type title).
The Translation File
For now, we just need the translation file, en-GB.plg_finder_stone_burials.ini, to hold the localized description.
# $Id$ # Copyright (C) 2009 New Life in IT Pty Ltd. All rights reserved. # License GNU General Public License # Note : All ini files need to be saved as UTF-8 STONE_BURIALS_FINDER_PLUGIN_DESC=This plugin indexes the Stone Orchard Burial cache in Joomla for use with Finder.
Installing the Plugin
You can either package these files together and install them in the normal Joomla way, or create them in-situ and then run the following database query to add the plugin to the database manually:
INSERT INTO `jos_plugins` VALUES (0 , 'Finder - Stone Orchard (for Joomla) Burials', 'stone_burials', 'finder', '0', '0', '1', '0', '0', '0', '0000-00-00 00:00:00', '');
Whichever method you choose, you should check that the plugin is showing in the Joomla Plugin Manager.
Designing the Indexer Methods
There are now a number of methods we need to add to the plugin in order for it to start indexing the content.
The _setup Method
The setup method is used to add any custom branches to the Finder content maps (examples of branches are categories and sections in Joomla articles).
/**
* Override setup to initialize custom Branches, etc
*
* @return boolean
*/
function _setup()
{
// Ensure the branches are added before we start
if ($this->_taxonomy->addBranch('Cemetery', 'cemetery', 1, 0) === false) {
$this->_subject->setError($this->_taxonomy->getError());
return false;
}
if ($this->_taxonomy->addBranch('Gender', 'gender', 1, 0) === false) {
$this->_subject->setError($this->_taxonomy->getError());
return false;
}
return true;
}
The main point of this method is to add the "Cemetery" branch (with an alias of "cemetery"). The last two arguments of the addBranch method are the published state and view access level respectively. Adding a branch like this will allow us to filter by the cemetery in the Finder advanced search.
You can add any number of branches as are required to suit your content. If you do not need to add any branches then you can omit this method, or include it and simple have it return true.
The _getUrl Method
The next method we add is the method that returns the base URL for the content given the ID for the content.
/**
* Gets the URL for the link for a given Id
*
* @param int $id The id of the burial record.
*
* @return string
*/
function _getUrl($id)
{
return 'index.php?option=com_stone&view=burial&id='.(int) $id;
}
The _getListQuery Method
This method does the job of retrieving the content data that to index. This is a fairly simple example because we are bringing back all the fields in the table and there is no related information in other tables.
/**
* The query for getting the list of items
*
* @return JxQuery
*/
function &_getListQuery()
{
jximport('jxtended.database.query');
$query = new JXQuery;
$query->select('a.*');
$query->from('#__stone_burials AS a');
return $query;
}
The method just returns a query that Finder will process during the indexing process.
The _indexItem Method
This method is the workhorse that takes the content data and assembles it in the right way for Finder to index.
/**
* Index a burial
*
* @param object
*
* @return boolean
*/
function _indexItem(&$item)
{
jimport('joomla.application.router');
require_once JPATH_SITE.DS.'includes'.DS.'application.php';
require_once JPATH_SITE.DS.'components'.DS.'com_stone'.DS.'router.php';
// We are routing the URL so that we can score the route.
$url = $this->_getUrl($item->id);
$route = StoneRoute::burial($item->id);
$sef = JXIndexerHelper::getContentRoute($route);
$misc = array();
$misc['meta'] = array();
// To improve search results, add associated data to be indexed (but don't store it).
if ($item->Undertaker_Last_Name) {
$misc['meta'][] = $item->Undertaker_Last_Name;
}
// Build the title.
$title = strtoupper($item->Family_Name);
if ($item->Family_Name)
{
if (!empty($title)) {
$title .= ', ';
}
$title .= $item->Given_Names;
}
// Build the text to index.
$text = 'Age: '.$item->Age_at_Death;
$text .= ', Gender: '.$item->Age_at_Death;
$text .= ', Died: '.$item->Date_of_Death;
$text .= ', Buried: '.$item->Date_of_Burial;
$text .= ', Cemetery: '.$item->Cemetery;
$text .= ', Location: '.$item->Location;
// Index the item
$linkId = JXIndexerHelper::index(
$url,
$route,
$sef,
$title,
'', // Meta description
'', // Meta keywords
$text, // The text to index
1, // The published state
$this->_type_id,
0, // The access level
0,
$misc,
$this->_config,
null, // Publishing Start Date
null, // Publishing End Date
$item->Date_of_Birth, // Content State Date
$item->Date_of_Burial // Content End Date
);
// Check for errors.
if (JError::isError($linkId)) {
$this->_subject->setError($linkId->getMessage());
return false;
}
// Remove the existing taxonomy maps
if (!$this->_taxonomy->removeMaps($linkId)) {
$this->setError($this->_taxonomy->getError());
return false;
}
if (!$this->_indexTaxonomy($item, $linkId)) {
return false;
}
return true;
}
The _indexTaxonomy Method
Finally, we add the method to index the taxonomy mappings.
/**
* Index the related taxonomy
*
* @param object The item being indexed
* @param int The current link ID that the taxonomy relates to
* @return boolean
*/
function _indexTaxonomy(&$item, $linkId)
{
// Add the class taxonomy map
if ($item->Cemetery)
{
if (!$this->_addMap($linkId, 'Cemetery', $item->Cemetery, '', 1, 0)) {
return false;
}
}
if ($item->Gender)
{
if (!$this->_addMap($linkId, 'Gender', $item->Gender, '', 1, 0)) {
return false;
}
}
return parent::_indexTaxonomy($item, $linkId);
}
No problem, support subscriptions give you access to one-on-one help from real Joomla experts.