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
                
            );
        },