Creating a search plugin - neicv/pagekit-search GitHub Wiki
Description
This document is about how to create a Search Plugin. You can use a Search Plugin to search through the database of your Pagekit site. To create a plugin, you will at least need two steps; an PHP file and a edit the following file: ..\packages\friendlyit\search\index.php.
PHP file
The PHP file of your plugin is the most important file of the plugin. This is an example PHP file of a search plugin. The comments are included. File location: ..\packages\friendlyit\search\src\Plugin\SearchNameofPlugin.php
<?php
//First start with information about the Plugin and yourself. For example:
/**
* @package Pagekit Extension
* @subpackage Search.content - Page
*
* @copyright Copyright
* @license License, for example GNU/GPL
*/
namespace Friendlyit\Search\Plugin;
use Friendlyit\Search\Helpers\EXSearchHelper;
use Friendlyit\Search\Event\SearchEvent;
use Pagekit\Component\Database\ORM\Repository;
use Pagekit\Event\EventSubscriberInterface;
use Pagekit\Application as App;
use Pagekit\Site\Model\Page;
/**
* All functions need to get wrapped in a class
*
* The class name should start with 'SearchNameofPlugin' followed by the name of the plugin (eg.: SearchPagePlugin, SearchBlogPlugin, SearchArticlePlugin) . Pagekit calls the class based on the name of the plugin, so it is very important that they match
*/
class SearchNameofPlugin implements EventSubscriberInterface
{
const PAGES_PER_PAGE = 50;
const STATUS_PUBLISHED = 1;
/**
* @var Repository
*/
// yourvariables
protected $yourvariables; //(eg. $pages for SearchPagePlugin)
/**
* @var Repository
*/
protected $roles;
/**
* SearchPage plugins callback.
*
* Determine areas searchable by this plugin.
*
* @return array An array of search areas.
*
* @since 0.1
*/
// Define a function to return an array of search areas. Replace 'nameofplugin' with the name of your plugin.
// Note the value of the array key is normally a language string
public function onContentSearchAreas(SearchEvent $event)
{
static $areas = array();
$areas = ['nameofplugin' => __('Nameofplugin')]; // eg. ($areas = ['pages' => __('Pages')];
$event->setSearchArray($areas);
}
public function onContentSearchAreasL()
{
static $areas = array();
$areas = ['nameofplugin' => __('Nameofplugin')];
return $areas;
}
/**
* Search content (Your content).
* The SQL must return the following fields that are used in a common display
* routine: href, title, section, created, text, browsernav.
*
* @param string $text Target search string.
* @param string $phrase Matching option (possible values: exact|any|all). Default is "any".
* @param string $ordering Ordering option (possible values: newest|oldest|popular|alpha|category). Default is "newest".
* @param mixed $areas An array if the search it to be restricted to areas or null to search all areas.
*
* @return array Search results.
*
* @since 0.1
*/
public function onContentSearch(SearchEvent $event)
{
// Now retrieve the plugin parameters like this:
$params = App::module('friendlyit/search')->config('defaults');
$limit = isset($params['limit_search_result']) ? $params['limit_search_result'] : self::PAGES_PER_PAGE;
$markdown = isset($params['markdown_enabled']) ? $params['markdown_enabled'] : true ;
$parameters = $event->getParameters();
$text = $parameters[0];
$phrase = $parameters[1];
$ordering = $parameters[2];
$areas = $parameters[3];
$searchText = $text;
if (is_array($areas))
{
if (count($areas))
{
if (!array_intersect($areas, array_keys($this->onContentSearchAreasL())))
{
$a = array();
$event->setSearchData($a);
return $a;
}
}
}
// Use the PHP function trim to delete spaces in front of or at the back of the searching terms
$text = trim($text);
if ($text == '')
{
// Return Array when nothing was filled in.
return array();
}
$text = EXSearchHelper::strip_data(trim($text));
$text = stripslashes($text);
$text = htmlspecialchars($text);
if (App::db()->getDatabasePlatform()->getName() === 'sqlite') $b_sqlite = true; else $b_sqlite = false;
// After this, you have to add the database part. This will be the most difficult part, because this changes per situation.
// In the coding examples later on you will find some of the examples used by Joomla! 3.1 core Search Plugins.
//It will look something like this.
$matches = array();
switch ($phrase)
{
case 'exact':
$text =App::db()->quote('%' . $text . '%', false);
$wheres2 = array();
$wheres2[] = 'a.title LIKE '.$text;
$wheres2[] = 'a.content LIKE '. $text;
$where = '(' . implode(') OR (', $wheres2) . ')';
break;
case 'all':
case 'any':
default:
$words = explode(' ', $text);
$wheres = array();
foreach ($words as $word)
{
$word = App::db()->quote('%' . $word . '%', false);
$wheres2 = array();
$wheres2[] = 'a.title LIKE '.$word;
$wheres2[] = 'a.content LIKE '.$word;
$wheres[] = implode(' OR ', $wheres2);
}
$where = '(' . implode(($phrase == 'all' ? ') AND (' : ') OR ('), $wheres) . ')';
break;
}
switch ($ordering)
{
case 'oldest':
$order = 'a.id, ASC';
break;
case 'popular':
//$order = "'a.hits DESC'";
//break;
case 'alpha':
$order = 'a.title, ASC';
break;
case 'category':
$order = 'a.title, ASC';
break;
case 'newest':
default:
$order = 'a.id, DESC';
break;
}
$rows = array();
// The database query; differs per situation! It will look something like this (example from Page search plugin):
$type = 'page';
$b = self::STATUS_PUBLISHED;
$aid = 'a.id';
$id_string = '"defaults":{"id":';
$matches['v00'] = "%{$type}%";
$matches['v0'] = "$b";
$where = '(c.status = :v0) AND (' . $where;
if (!$b_sqlite) {$concatestr = 'CONCAT (\'%"defaults":{"id":\', cast(a.id as char),\'}%\')';}
else {$concatestr = '(\'%"defaults":{"id":\'|| cast(a.id as char) || \'}%\')';}
$query = App::db()->createQueryBuilder()
->from('@system_page a')
->join('@system_node c', '(c.type LIKE :v00 AND c.data LIKE '.$concatestr.')', 'INNER')
->Where( $where .')', $matches);
$orders = explode(",", $order);
if ($orders) {
$query->orderBy($orders[0], $orders[1]);
}
/**
* Creates and adds an "order by" to the query.
*
* @param string $sort
* @param string $order
* @return self
*/
$query->groupBy('a.title, a.content');
//$query->offset($page * $limit)->limit($limit);
$query->offset(0)->limit($limit);
$query->where(function ($query) { return $query->where('roles IS NULL')->whereInSet('roles', App::user()->roles, false, 'OR');});
$rows = $query->get();
$user = App::user();
$list = null;
$index = '0';
if (!empty($rows))
{
foreach ($rows as $key => $item)
{
$list[$index]= new \stdclass();
$list[$index]->title = $item['title'];
$list[$index]->metadesc = '';
$list[$index]->metakey = '';
$list[$index]->created = '';
$list[$index]->text = App::content()->applyPlugins($item['content'], ['item' => $item, 'markdown' => $markdown]);
$list[$index]->section = __('Uncategorised'); // PAGE NOT HAVING A SECTION
$list[$index]->catslug = '';
$list[$index]->browsernav = '';
$list[$index]->href = App::url($item['link']);
$index++;
}
$rows[] = $list;
}
$results = array();
if (count($rows))
{
foreach ($rows as $row)
{
$new_row = array();
foreach ($row as $article)
{
if (EXSearchHelper::checkNoHTML($article, $searchText, array('text', 'title')))
{
$new_row[] = $article;
}
}
$results = array_merge($results, (array) $new_row);
}
}
//Return the search results in an array
$event->setSearchData($results);
return array();
}
/**
* {@inheritdoc}
*/
public function subscribe()
{
return [
'search.onContentSearchAreas' => ['onContentSearchAreas', 5],
'search.onContentSearch' => ['onContentSearch', 5]
];
}
}
There are four variables that get passed in. They are evident by their names and use in the above code. What's not obvious is what the function should return: an array of objects that the search tool uses to display the results. The results could alternatively have been assembled like this.
$index = '0';
if (!empty($rows))
// ----- "PDO" -----
{
foreach ($rows as $key => $item)
{
$list[$index]= new \stdclass();
$list[$index]->title = $item['title'];
$list[$index]->metadesc = '';
$list[$index]->metakey = '';
$list[$index]->created = $item['date'];
//$list[$index]->text = $item['text'];
$list[$index]->text = App::content()->applyPlugins($item['text'], ['item' => $item, 'markdown' => $markdown]);
$list[$index]->section = __('Blog'); // PAGE NOT HAVING A SECTION
$list[$index]->catslug = '';
$list[$index]->browsernav = '';
$list[$index]->href = App::url('@blog/id', ['id' => $item['id']], true);
$list[$index]->id = $item['id'];
$index++;
}
$rows[] = $list;
}
Edit index.php file
Edit the following file: ..\packages\friendlyit\search\index.php
You must enter a description and initialize your plugin:
<?php
// Announce your plugin:
use Friendlyit\Search\Plugin\SearchNameofPlugin;
use Friendlyit\Search\Plugin\SearchPagePlugin;
use Friendlyit\Search\Plugin\SearchBlogPlugin;
return [
'name' => 'friendlyit/search',
'type' => 'extension',
'autoload' => [
// -----------------------------------------------------
'events' => [
'boot' => function ($event, $app) {
$app->subscribe(
// initialize your plugin (subscribe)
new SearchNameofPlugin,
new SearchPagePlugin,
new SearchBlogPlugin
);
},