Merge branch 'JamesForks-psr4'
This commit is contained in:
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
* text=auto
|
||||||
|
|
||||||
|
/tests export-ignore
|
||||||
|
/.gitattributes export-ignore
|
||||||
|
/.gitignore export-ignore
|
||||||
|
/.travis.yml export-ignore
|
||||||
|
/phpunit.xml export-ignore
|
||||||
|
/README.md export-ignore
|
@ -17,8 +17,8 @@
|
|||||||
"phpspec/phpspec": "~2.0"
|
"phpspec/phpspec": "~2.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {
|
"psr-4": {
|
||||||
"TomLingham": "src/"
|
"TomLingham\\Searchy\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "stable"
|
"minimum-stability": "stable"
|
||||||
|
25
config/searchy.php
Executable file
25
config/searchy.php
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'default' => 'fuzzy',
|
||||||
|
|
||||||
|
'fieldName' => 'relevance',
|
||||||
|
|
||||||
|
'drivers' => [
|
||||||
|
|
||||||
|
'fuzzy' => [
|
||||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\FuzzySearchDriver',
|
||||||
|
],
|
||||||
|
|
||||||
|
'simple' => [
|
||||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\SimpleSearchDriver',
|
||||||
|
],
|
||||||
|
|
||||||
|
'levenshtein' => [
|
||||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\LevenshteinSearchDriver',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
21
src/Facades/Searchy.php
Executable file
21
src/Facades/Searchy.php
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Facades;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searchy facade for the Laravel framework.
|
||||||
|
*/
|
||||||
|
class Searchy extends Facade
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the registered component.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'searchy';
|
||||||
|
}
|
||||||
|
}
|
16
src/Interfaces/MatcherInterface.php
Executable file
16
src/Interfaces/MatcherInterface.php
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Interfaces;
|
||||||
|
|
||||||
|
interface MatcherInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Builds the string to add to the SELECT statement for the Matcher.
|
||||||
|
*
|
||||||
|
* @param $column
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function buildQueryString($column, $searchString);
|
||||||
|
}
|
12
src/Interfaces/SearchDriverInterface.php
Executable file
12
src/Interfaces/SearchDriverInterface.php
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Interfaces;
|
||||||
|
|
||||||
|
interface SearchDriverInterface
|
||||||
|
{
|
||||||
|
public function query($searchString);
|
||||||
|
|
||||||
|
public function select(/* $columns */);
|
||||||
|
|
||||||
|
public function get();
|
||||||
|
}
|
30
src/Matchers/AcronymMatcher.php
Executable file
30
src/Matchers/AcronymMatcher.php
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches.
|
||||||
|
*
|
||||||
|
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'.
|
||||||
|
*
|
||||||
|
* Class AcronymMatcher
|
||||||
|
*/
|
||||||
|
class AcronymMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
||||||
|
|
||||||
|
return implode('% ', str_split(strtoupper($searchString))).'%';
|
||||||
|
}
|
||||||
|
}
|
36
src/Matchers/BaseMatcher.php
Executable file
36
src/Matchers/BaseMatcher.php
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
use TomLingham\Searchy\Interfaces\MatcherInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property mixed multiplier
|
||||||
|
* @property mixed operator
|
||||||
|
*/
|
||||||
|
abstract class BaseMatcher implements MatcherInterface
|
||||||
|
{
|
||||||
|
protected $multiplier;
|
||||||
|
|
||||||
|
public function __construct($multiplier)
|
||||||
|
{
|
||||||
|
$this->multiplier = $multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default process for building the Query string.
|
||||||
|
*
|
||||||
|
* @param $column
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function buildQueryString($column, $searchString)
|
||||||
|
{
|
||||||
|
if (method_exists($this, 'formatSearchString')) {
|
||||||
|
$searchString = $this->formatSearchString($searchString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "IF($column {$this->operator} '$searchString', {$this->multiplier}, 0)";
|
||||||
|
}
|
||||||
|
}
|
44
src/Matchers/ConsecutiveCharactersMatcher.php
Executable file
44
src/Matchers/ConsecutiveCharactersMatcher.php
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches strings that include all the characters in the search relatively position within the string.
|
||||||
|
* It also calculates the percentage of characters in the string that are matched and applies the multiplier accordingly.
|
||||||
|
*
|
||||||
|
* For Example, a search for 'fba' would match; 'Foo Bar' or 'Afraid of bats'
|
||||||
|
*
|
||||||
|
* Class ConsecutiveCharactersMatcher
|
||||||
|
*/
|
||||||
|
class ConsecutiveCharactersMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
||||||
|
|
||||||
|
return '%'.implode('%', str_split($searchString)).'%';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $column
|
||||||
|
* @param $rawString
|
||||||
|
*
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function buildQueryString($column, $rawString)
|
||||||
|
{
|
||||||
|
$searchString = $this->formatSearchString($rawString);
|
||||||
|
|
||||||
|
return "IF(REPLACE($column, '\.', '') {$this->operator} '$searchString', ROUND({$this->multiplier} * (CHAR_LENGTH( '$rawString' ) / CHAR_LENGTH( REPLACE($column, ' ', '') ))), 0)";
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,18 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
<?php
|
||||||
|
|
||||||
/**
|
namespace TomLingham\Searchy\Matchers;
|
||||||
* Matches an exact string and applies a high multiplier to bring any exact matches to the top
|
|
||||||
* When sanitize is on, if the expression strips some of the characters from the search query
|
/**
|
||||||
* then this may not be able to match against a string despite entering in an exact match.
|
* Matches an exact string and applies a high multiplier to bring any exact matches to the top
|
||||||
*
|
* When sanitize is on, if the expression strips some of the characters from the search query
|
||||||
* Class ExactMatcher
|
* then this may not be able to match against a string despite entering in an exact match.
|
||||||
* @package TomLingham\Searchy\Matchers
|
*
|
||||||
*/
|
* Class ExactMatcher
|
||||||
|
*/
|
||||||
class ExactMatcher extends BaseMatcher
|
class ExactMatcher extends BaseMatcher
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
/**
|
* @var string
|
||||||
* @var string
|
*/
|
||||||
*/
|
protected $operator = '=';
|
||||||
protected $operator = '=';
|
}
|
||||||
|
|
||||||
}
|
|
28
src/Matchers/InStringMatcher.php
Executable file
28
src/Matchers/InStringMatcher.php
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches against any occurrences of a string within a string and is case-insensitive.
|
||||||
|
*
|
||||||
|
* For example, a search for 'smi' would match; 'John Smith' or 'Smiley Face'
|
||||||
|
*
|
||||||
|
* Class InStringMatcher
|
||||||
|
*/
|
||||||
|
class InStringMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
return "%$searchString%";
|
||||||
|
}
|
||||||
|
}
|
24
src/Matchers/LevenshteinMatcher.php
Executable file
24
src/Matchers/LevenshteinMatcher.php
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches.
|
||||||
|
*
|
||||||
|
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'.
|
||||||
|
*
|
||||||
|
* Class AcronymMatcher
|
||||||
|
*/
|
||||||
|
class LevenshteinMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param $column
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function buildQueryString($column, $searchString)
|
||||||
|
{
|
||||||
|
return "levenshtein($column, '$searchString')";
|
||||||
|
}
|
||||||
|
}
|
28
src/Matchers/StartOfStringMatcher.php
Executable file
28
src/Matchers/StartOfStringMatcher.php
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches Strings that begin with the search string.
|
||||||
|
*
|
||||||
|
* For example, a search for 'hel' would match; 'Hello World' or 'helping hand'
|
||||||
|
*
|
||||||
|
* Class StartOfStringMatcher
|
||||||
|
*/
|
||||||
|
class StartOfStringMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
return "$searchString%";
|
||||||
|
}
|
||||||
|
}
|
28
src/Matchers/StartOfWordsMatcher.php
Executable file
28
src/Matchers/StartOfWordsMatcher.php
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the start of each word against each word in a search.
|
||||||
|
*
|
||||||
|
* For example, a search for 'jo ta' would match; 'John Taylor' or 'Joshua B. Takashi'
|
||||||
|
*
|
||||||
|
* Class StartOfWordsMatcher
|
||||||
|
*/
|
||||||
|
class StartOfWordsMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
return implode('% ', explode(' ', $searchString)).'%';
|
||||||
|
}
|
||||||
|
}
|
35
src/Matchers/StudlyCaseMatcher.php
Executable file
35
src/Matchers/StudlyCaseMatcher.php
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\Matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches Studly Case strings using the first letters of the words only.
|
||||||
|
*
|
||||||
|
* For example a search for 'hp' would match; 'HtmlServiceProvider' or 'HashParser' but not 'hasProvider'
|
||||||
|
*
|
||||||
|
* Class StudlyCaseMatcher
|
||||||
|
*/
|
||||||
|
class StudlyCaseMatcher extends BaseMatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $operator = 'LIKE BINARY';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function formatSearchString($searchString)
|
||||||
|
{
|
||||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
||||||
|
|
||||||
|
return implode('%', str_split(strtoupper($searchString))).'%';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildQueryString($column, $searchString)
|
||||||
|
{
|
||||||
|
return "IF( CHAR_LENGTH( TRIM($column)) = CHAR_LENGTH( REPLACE( TRIM($column), ' ', '')) AND $column {$this->operator} '{$this->formatSearchString($searchString)}', {$this->multiplier}, 0)";
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,29 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
<?php
|
||||||
|
|
||||||
/**
|
namespace TomLingham\Searchy\Matchers;
|
||||||
* Matches a string based on how many times the search string appears inside the string
|
|
||||||
* it then applies the multiplier for each occurrence.
|
/**
|
||||||
*
|
* Matches a string based on how many times the search string appears inside the string
|
||||||
* For example, a search for 'tha' would match; 'I hope that that cat has caught that mouse' (3 x multiplier) or 'Thanks, it was great!' (1 x multiplier)
|
* it then applies the multiplier for each occurrence.
|
||||||
*
|
*
|
||||||
* Class TimesInStringMatcher
|
* For example, a search for 'tha' would match; 'I hope that that cat has caught that mouse' (3 x multiplier) or 'Thanks, it was great!' (1 x multiplier)
|
||||||
* @package TomLingham\Searchy\Matchers
|
*
|
||||||
*/
|
* Class TimesInStringMatcher
|
||||||
|
*/
|
||||||
class TimesInStringMatcher extends BaseMatcher
|
class TimesInStringMatcher extends BaseMatcher
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
/**
|
* @param $column
|
||||||
* @param $column
|
* @param $searchString
|
||||||
* @param $searchString
|
*
|
||||||
* @return mixed|string
|
* @return mixed|string
|
||||||
*/
|
*/
|
||||||
public function buildQueryString( $column, $searchString )
|
public function buildQueryString($column, $searchString)
|
||||||
{
|
{
|
||||||
$query = "{$this->multiplier} * ROUND ((
|
$query = "{$this->multiplier} * ROUND ((
|
||||||
CHAR_LENGTH($column) - CHAR_LENGTH( REPLACE ( LOWER($column), lower('$searchString'), ''))
|
CHAR_LENGTH($column) - CHAR_LENGTH( REPLACE ( LOWER($column), lower('$searchString'), ''))
|
||||||
) / LENGTH('$searchString'))";
|
) / LENGTH('$searchString'))";
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
}
|
}
|
111
src/SearchBuilder.php
Executable file
111
src/SearchBuilder.php
Executable file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy;
|
||||||
|
|
||||||
|
use Illuminate\Config\Repository;
|
||||||
|
use TomLingham\Searchy\SearchDrivers\FuzzySearchDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property mixed driverName
|
||||||
|
*/
|
||||||
|
class SearchBuilder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
private $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
private $searchFields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
private $driverName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
public function __construct(Repository $config)
|
||||||
|
{
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchable
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function search($searchable)
|
||||||
|
{
|
||||||
|
if (is_object($searchable) && method_exists($searchable, 'getTable')) {
|
||||||
|
$this->table = $searchable->getTable();
|
||||||
|
} else {
|
||||||
|
$this->table = $searchable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FuzzySearchDriver
|
||||||
|
*/
|
||||||
|
public function fields(/* $fields */)
|
||||||
|
{
|
||||||
|
$this->searchFields = func_get_args();
|
||||||
|
|
||||||
|
return $this->makeDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $driverName
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function driver($driverName)
|
||||||
|
{
|
||||||
|
$this->driverName = $driverName;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $table
|
||||||
|
* @param $searchFields
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __call($table, $searchFields)
|
||||||
|
{
|
||||||
|
return call_user_func_array([$this->search($table), 'fields'], $searchFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function makeDriver()
|
||||||
|
{
|
||||||
|
$relevanceFieldName = $this->config->get('searchy.fieldName');
|
||||||
|
|
||||||
|
// Check if default driver is being overridden, otherwise
|
||||||
|
// load the default
|
||||||
|
if ($this->driverName) {
|
||||||
|
$driverName = $this->driverName;
|
||||||
|
} else {
|
||||||
|
$driverName = $this->config->get('searchy.default');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the details for the selected driver from the configuration file
|
||||||
|
$driver = $this->config->get("searchy.drivers.$driverName")['class'];
|
||||||
|
|
||||||
|
// Create a new instance of the selected drivers 'class' and pass
|
||||||
|
// through table and fields to search
|
||||||
|
$driverInstance = new $driver($this->table, $this->searchFields, $relevanceFieldName);
|
||||||
|
|
||||||
|
return $driverInstance;
|
||||||
|
}
|
||||||
|
}
|
154
src/SearchDrivers/BaseSearchDriver.php
Executable file
154
src/SearchDrivers/BaseSearchDriver.php
Executable file
@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\SearchDrivers;
|
||||||
|
|
||||||
|
use TomLingham\Searchy\Interfaces\SearchDriverInterface;
|
||||||
|
|
||||||
|
abstract class BaseSearchDriver implements SearchDriverInterface
|
||||||
|
{
|
||||||
|
protected $table;
|
||||||
|
protected $columns;
|
||||||
|
protected $searchFields;
|
||||||
|
protected $searchString;
|
||||||
|
protected $relevanceFieldName;
|
||||||
|
protected $query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null $table
|
||||||
|
* @param array $searchFields
|
||||||
|
* @param $relevanceFieldName
|
||||||
|
* @param array $columns
|
||||||
|
*
|
||||||
|
* @internal param $relevanceField
|
||||||
|
*/
|
||||||
|
public function __construct($table = null, $searchFields = [], $relevanceFieldName, $columns = ['*'])
|
||||||
|
{
|
||||||
|
$this->searchFields = $searchFields;
|
||||||
|
$this->table = $table;
|
||||||
|
$this->columns = $columns;
|
||||||
|
$this->relevanceFieldName = $relevanceFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify which columns to return.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function select()
|
||||||
|
{
|
||||||
|
$this->columns = func_get_args();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the string that is is being searched for.
|
||||||
|
*
|
||||||
|
* @param $searchString
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Query\Builder|mixed|static
|
||||||
|
*/
|
||||||
|
public function query($searchString)
|
||||||
|
{
|
||||||
|
$this->searchString = trim(\DB::connection()->getPdo()->quote($searchString), "'");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the results of the search as an Array.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->run()->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance of the Laravel Fluent Database Query Object with the search
|
||||||
|
* queries applied.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getQuery()
|
||||||
|
{
|
||||||
|
return $this->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the 'having' method directly on the Laravel Fluent Database Query Object
|
||||||
|
* and returns the instance of the object.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function having()
|
||||||
|
{
|
||||||
|
return call_user_func_array([$this->run(), 'having'], func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function run()
|
||||||
|
{
|
||||||
|
$this->query = \DB::table($this->table)
|
||||||
|
->select($this->columns)
|
||||||
|
->addSelect($this->buildSelectQuery($this->searchFields))
|
||||||
|
->orderBy($this->relevanceFieldName, 'desc')
|
||||||
|
->having($this->relevanceFieldName, '>', 0);
|
||||||
|
|
||||||
|
return $this->query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $searchFields
|
||||||
|
*
|
||||||
|
* @return array|\Illuminate\Database\Query\Expression
|
||||||
|
*/
|
||||||
|
protected function buildSelectQuery(array $searchFields)
|
||||||
|
{
|
||||||
|
$query = [];
|
||||||
|
|
||||||
|
foreach ($searchFields as $searchField) {
|
||||||
|
if (strpos($searchField, '::')) {
|
||||||
|
$concatString = str_replace('::', ", ' ', ", $searchField);
|
||||||
|
$query[] = $this->buildSelectCriteria("CONCAT({$concatString})");
|
||||||
|
} else {
|
||||||
|
$query[] = $this->buildSelectCriteria($searchField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \DB::raw(implode(' + ', $query).' AS '.$this->relevanceFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null $searchField
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function buildSelectCriteria($searchField = null)
|
||||||
|
{
|
||||||
|
$criteria = [];
|
||||||
|
|
||||||
|
foreach ($this->matchers as $matcher => $multiplier) {
|
||||||
|
$criteria[] = $this->makeMatcher($searchField, $matcher, $multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' + ', $criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $searchField
|
||||||
|
* @param $matcherClass
|
||||||
|
* @param $multiplier
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function makeMatcher($searchField, $matcherClass, $multiplier)
|
||||||
|
{
|
||||||
|
$matcher = new $matcherClass($multiplier);
|
||||||
|
|
||||||
|
return $matcher->buildQueryString($searchField, $this->searchString);
|
||||||
|
}
|
||||||
|
}
|
20
src/SearchDrivers/FuzzySearchDriver.php
Executable file
20
src/SearchDrivers/FuzzySearchDriver.php
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\SearchDrivers;
|
||||||
|
|
||||||
|
class FuzzySearchDriver extends BaseSearchDriver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $matchers = [
|
||||||
|
'TomLingham\Searchy\Matchers\ExactMatcher' => 100,
|
||||||
|
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50,
|
||||||
|
'TomLingham\Searchy\Matchers\AcronymMatcher' => 42,
|
||||||
|
'TomLingham\Searchy\Matchers\ConsecutiveCharactersMatcher' => 40,
|
||||||
|
'TomLingham\Searchy\Matchers\StartOfWordsMatcher' => 35,
|
||||||
|
'TomLingham\Searchy\Matchers\StudlyCaseMatcher' => 32,
|
||||||
|
'TomLingham\Searchy\Matchers\InStringMatcher' => 30,
|
||||||
|
'TomLingham\Searchy\Matchers\TimesInStringMatcher' => 8,
|
||||||
|
];
|
||||||
|
}
|
13
src/SearchDrivers/LevenshteinSearchDriver.php
Executable file
13
src/SearchDrivers/LevenshteinSearchDriver.php
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\SearchDrivers;
|
||||||
|
|
||||||
|
class LevenshteinSearchDriver extends BaseSearchDriver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $matchers = [
|
||||||
|
'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100,
|
||||||
|
];
|
||||||
|
}
|
15
src/SearchDrivers/SimpleSearchDriver.php
Executable file
15
src/SearchDrivers/SimpleSearchDriver.php
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy\SearchDrivers;
|
||||||
|
|
||||||
|
class SimpleSearchDriver extends BaseSearchDriver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $matchers = [
|
||||||
|
'TomLingham\Searchy\Matchers\ExactMatcher' => 100,
|
||||||
|
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50,
|
||||||
|
'TomLingham\Searchy\Matchers\InStringMatcher' => 30,
|
||||||
|
];
|
||||||
|
}
|
56
src/SearchyServiceProvider.php
Executable file
56
src/SearchyServiceProvider.php
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TomLingham\Searchy;
|
||||||
|
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class SearchyServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates if loading of the provider is deferred.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $defer = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the service provider.
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers searchy.
|
||||||
|
*/
|
||||||
|
public function registerSearchy()
|
||||||
|
{
|
||||||
|
$this->app->bindShared('searchy', function ($app) {
|
||||||
|
return new SearchBuilder($app['config']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the configuration file.
|
||||||
|
*/
|
||||||
|
public function setupConfig()
|
||||||
|
{
|
||||||
|
$source = realpath(__DIR__.'/../config/searchy.php');
|
||||||
|
|
||||||
|
if (class_exists('Illuminate\Foundation\Application', false)) {
|
||||||
|
$this->publishes([$source => config_path('searchy.php')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mergeConfigFrom($source, 'searchy');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot the service provider.
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->setupConfig();
|
||||||
|
$this->registerSearchy();
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Facades;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Facade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searchy facade for the Laravel framework
|
|
||||||
*/
|
|
||||||
class Searchy extends Facade
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the registered component.
|
|
||||||
*
|
|
||||||
* @return object
|
|
||||||
*/
|
|
||||||
protected static function getFacadeAccessor()
|
|
||||||
{
|
|
||||||
return 'searchy';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Interfaces;
|
|
||||||
|
|
||||||
interface MatcherInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Builds the string to add to the SELECT statement for the Matcher
|
|
||||||
*
|
|
||||||
* @param $column
|
|
||||||
* @param $searchString
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function buildQueryString( $column, $searchString );
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Interfaces;
|
|
||||||
|
|
||||||
interface SearchDriverInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
public function query( $searchString );
|
|
||||||
|
|
||||||
public function select( /* $columns */ );
|
|
||||||
|
|
||||||
public function get();
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches
|
|
||||||
*
|
|
||||||
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'.
|
|
||||||
*
|
|
||||||
* Class AcronymMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class AcronymMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return mixed|string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
|
||||||
return implode( '% ', str_split(strtoupper( $searchString ))) . '%';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
use TomLingham\Searchy\Interfaces\MatcherInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property mixed multiplier
|
|
||||||
* @property mixed operator
|
|
||||||
*/
|
|
||||||
abstract class BaseMatcher implements MatcherInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
protected $multiplier;
|
|
||||||
|
|
||||||
public function __construct( $multiplier )
|
|
||||||
{
|
|
||||||
$this->multiplier = $multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default process for building the Query string
|
|
||||||
*
|
|
||||||
* @param $column
|
|
||||||
* @param $searchString
|
|
||||||
* @return mixed|string
|
|
||||||
*/
|
|
||||||
public function buildQueryString( $column, $searchString )
|
|
||||||
{
|
|
||||||
if ( method_exists($this, 'formatSearchString') )
|
|
||||||
$searchString = $this->formatSearchString( $searchString );
|
|
||||||
|
|
||||||
return "IF($column {$this->operator} '$searchString', {$this->multiplier}, 0)";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches strings that include all the characters in the search relatively position within the string.
|
|
||||||
* It also calculates the percentage of characters in the string that are matched and applies the multiplier accordingly.
|
|
||||||
*
|
|
||||||
* For Example, a search for 'fba' would match; 'Foo Bar' or 'Afraid of bats'
|
|
||||||
*
|
|
||||||
* Class ConsecutiveCharactersMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
class ConsecutiveCharactersMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
|
||||||
return '%'.implode('%', str_split( $searchString )).'%';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $column
|
|
||||||
* @param $rawString
|
|
||||||
* @return mixed|string
|
|
||||||
*/
|
|
||||||
public function buildQueryString( $column, $rawString )
|
|
||||||
{
|
|
||||||
$searchString = $this->formatSearchString( $rawString );
|
|
||||||
|
|
||||||
return "IF(REPLACE($column, '\.', '') {$this->operator} '$searchString', ROUND({$this->multiplier} * (CHAR_LENGTH( '$rawString' ) / CHAR_LENGTH( REPLACE($column, ' ', '') ))), 0)";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches against any occurrences of a string within a string and is case-insensitive.
|
|
||||||
*
|
|
||||||
* For example, a search for 'smi' would match; 'John Smith' or 'Smiley Face'
|
|
||||||
*
|
|
||||||
* Class InStringMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class InStringMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
return "%$searchString%";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches
|
|
||||||
*
|
|
||||||
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'.
|
|
||||||
*
|
|
||||||
* Class AcronymMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LevenshteinMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $column
|
|
||||||
* @param $searchString
|
|
||||||
* @return mixed|string
|
|
||||||
*/
|
|
||||||
public function buildQueryString( $column, $searchString )
|
|
||||||
{
|
|
||||||
return "levenshtein($column, '$searchString')";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches Strings that begin with the search string.
|
|
||||||
*
|
|
||||||
* For example, a search for 'hel' would match; 'Hello World' or 'helping hand'
|
|
||||||
*
|
|
||||||
* Class StartOfStringMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class StartOfStringMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
return "$searchString%";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches the start of each word against each word in a search
|
|
||||||
*
|
|
||||||
* For example, a search for 'jo ta' would match; 'John Taylor' or 'Joshua B. Takashi'
|
|
||||||
*
|
|
||||||
* Class StartOfWordsMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class StartOfWordsMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
return implode('% ', explode(' ', $searchString)) . '%';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches Studly Case strings using the first letters of the words only
|
|
||||||
*
|
|
||||||
* For example a search for 'hp' would match; 'HtmlServiceProvider' or 'HashParser' but not 'hasProvider'
|
|
||||||
*
|
|
||||||
* Class StudlyCaseMatcher
|
|
||||||
* @package TomLingham\Searchy\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
class StudlyCaseMatcher extends BaseMatcher
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $operator = 'LIKE BINARY';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchString
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatSearchString( $searchString )
|
|
||||||
{
|
|
||||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString);
|
|
||||||
return implode( '%', str_split(strtoupper( $searchString ))) . '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildQueryString( $column, $searchString )
|
|
||||||
{
|
|
||||||
return "IF( CHAR_LENGTH( TRIM($column)) = CHAR_LENGTH( REPLACE( TRIM($column), ' ', '')) AND $column {$this->operator} '{$this->formatSearchString($searchString)}', {$this->multiplier}, 0)";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy;
|
|
||||||
|
|
||||||
use Illuminate\Config\Repository;
|
|
||||||
use TomLingham\Searchy\SearchDrivers\FuzzySearchDriver;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property mixed driverName
|
|
||||||
*/
|
|
||||||
class SearchBuilder {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
private $table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
private $searchFields;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
private $driverName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct( Repository $config )
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchable
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function search( $searchable )
|
|
||||||
{
|
|
||||||
if (is_object( $searchable ) && method_exists($searchable, 'getTable')) {
|
|
||||||
$this->table = $searchable->getTable();
|
|
||||||
} else {
|
|
||||||
$this->table = $searchable;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return FuzzySearchDriver
|
|
||||||
*/
|
|
||||||
public function fields( /* $fields */ )
|
|
||||||
{
|
|
||||||
$this->searchFields = func_get_args();
|
|
||||||
|
|
||||||
return $this->makeDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $driverName
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function driver( $driverName )
|
|
||||||
{
|
|
||||||
$this->driverName = $driverName;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $table
|
|
||||||
* @param $searchFields
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __call( $table, $searchFields )
|
|
||||||
{
|
|
||||||
return call_user_func_array([$this->search( $table ), 'fields'], $searchFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
private function makeDriver()
|
|
||||||
{
|
|
||||||
$relevanceFieldName = $this->config->get('searchy.fieldName');
|
|
||||||
|
|
||||||
// Check if default driver is being overridden, otherwise
|
|
||||||
// load the default
|
|
||||||
if ( $this->driverName ){
|
|
||||||
$driverName = $this->driverName;
|
|
||||||
} else {
|
|
||||||
$driverName = $this->config->get('searchy.default');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the details for the selected driver from the configuration file
|
|
||||||
$driver = $this->config->get("searchy.drivers.$driverName")['class'];
|
|
||||||
|
|
||||||
// Create a new instance of the selected drivers 'class' and pass
|
|
||||||
// through table and fields to search
|
|
||||||
$driverInstance = new $driver( $this->table, $this->searchFields, $relevanceFieldName );
|
|
||||||
return $driverInstance;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\SearchDrivers;
|
|
||||||
|
|
||||||
use TomLingham\Searchy\Interfaces\SearchDriverInterface;
|
|
||||||
|
|
||||||
|
|
||||||
abstract class BaseSearchDriver implements SearchDriverInterface {
|
|
||||||
|
|
||||||
protected $table;
|
|
||||||
protected $columns;
|
|
||||||
protected $searchFields;
|
|
||||||
protected $searchString;
|
|
||||||
protected $relevanceFieldName;
|
|
||||||
protected $query;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param null $table
|
|
||||||
* @param array $searchFields
|
|
||||||
* @param $relevanceFieldName
|
|
||||||
* @param array $columns
|
|
||||||
* @internal param $relevanceField
|
|
||||||
*/
|
|
||||||
public function __construct( $table = null, $searchFields = [], $relevanceFieldName, $columns = ['*'] )
|
|
||||||
{
|
|
||||||
$this->searchFields = $searchFields;
|
|
||||||
$this->table = $table;
|
|
||||||
$this->columns = $columns;
|
|
||||||
$this->relevanceFieldName = $relevanceFieldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify which columns to return
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function select()
|
|
||||||
{
|
|
||||||
$this->columns = func_get_args();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the string that is is being searched for
|
|
||||||
*
|
|
||||||
* @param $searchString
|
|
||||||
* @return \Illuminate\Database\Query\Builder|mixed|static
|
|
||||||
*/
|
|
||||||
public function query( $searchString )
|
|
||||||
{
|
|
||||||
$this->searchString = trim(\DB::connection()->getPdo()->quote( $searchString ), "'");
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the results of the search as an Array
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
return $this->run()->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an instance of the Laravel Fluent Database Query Object with the search
|
|
||||||
* queries applied
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getQuery()
|
|
||||||
{
|
|
||||||
return $this->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the 'having' method directly on the Laravel Fluent Database Query Object
|
|
||||||
* and returns the instance of the object
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function having()
|
|
||||||
{
|
|
||||||
return call_user_func_array([$this->run(), 'having'], func_get_args());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
protected function run()
|
|
||||||
{
|
|
||||||
$this->query = \DB::table( $this->table )
|
|
||||||
->select( $this->columns )
|
|
||||||
->addSelect( $this->buildSelectQuery( $this->searchFields ) )
|
|
||||||
->orderBy( $this->relevanceFieldName, 'desc' )
|
|
||||||
->having( $this->relevanceFieldName, '>', 0 );
|
|
||||||
|
|
||||||
return $this->query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $searchFields
|
|
||||||
* @return array|\Illuminate\Database\Query\Expression
|
|
||||||
*/
|
|
||||||
protected function buildSelectQuery( array $searchFields )
|
|
||||||
{
|
|
||||||
$query = [];
|
|
||||||
|
|
||||||
foreach ($searchFields as $searchField) {
|
|
||||||
if (strpos($searchField, '::')){
|
|
||||||
$concatString = str_replace('::', ", ' ', ", $searchField);
|
|
||||||
$query[] = $this->buildSelectCriteria( "CONCAT({$concatString})");
|
|
||||||
} else {
|
|
||||||
$query[] = $this->buildSelectCriteria( $searchField );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return \DB::raw(implode(' + ', $query) . ' AS ' . $this->relevanceFieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param null $searchField
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function buildSelectCriteria( $searchField = null )
|
|
||||||
{
|
|
||||||
$criteria = [];
|
|
||||||
|
|
||||||
foreach( $this->matchers as $matcher => $multiplier){
|
|
||||||
$criteria[] = $this->makeMatcher( $searchField, $matcher, $multiplier );
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(' + ', $criteria);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $searchField
|
|
||||||
* @param $matcherClass
|
|
||||||
* @param $multiplier
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function makeMatcher( $searchField, $matcherClass, $multiplier )
|
|
||||||
{
|
|
||||||
$matcher = new $matcherClass( $multiplier );
|
|
||||||
|
|
||||||
return $matcher->buildQueryString( $searchField, $this->searchString );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\SearchDrivers;
|
|
||||||
|
|
||||||
class FuzzySearchDriver extends BaseSearchDriver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $matchers = [
|
|
||||||
'TomLingham\Searchy\Matchers\ExactMatcher' => 100,
|
|
||||||
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50,
|
|
||||||
'TomLingham\Searchy\Matchers\AcronymMatcher' => 42,
|
|
||||||
'TomLingham\Searchy\Matchers\ConsecutiveCharactersMatcher' => 40,
|
|
||||||
'TomLingham\Searchy\Matchers\StartOfWordsMatcher' => 35,
|
|
||||||
'TomLingham\Searchy\Matchers\StudlyCaseMatcher' => 32,
|
|
||||||
'TomLingham\Searchy\Matchers\InStringMatcher' => 30,
|
|
||||||
'TomLingham\Searchy\Matchers\TimesInStringMatcher' => 8,
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\SearchDrivers;
|
|
||||||
|
|
||||||
class LevenshteinSearchDriver extends BaseSearchDriver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $matchers = [
|
|
||||||
'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy\SearchDrivers;
|
|
||||||
|
|
||||||
class SimpleSearchDriver extends BaseSearchDriver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $matchers = [
|
|
||||||
'TomLingham\Searchy\Matchers\ExactMatcher' => 100,
|
|
||||||
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50,
|
|
||||||
'TomLingham\Searchy\Matchers\InStringMatcher' => 30,
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
<?php namespace TomLingham\Searchy;
|
|
||||||
|
|
||||||
use Illuminate\Config\Repository;
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
|
||||||
|
|
||||||
class SearchyServiceProvider extends ServiceProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if loading of the provider is deferred.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $defer = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the service provider.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
$this->app->bindShared('searchy', function( $app )
|
|
||||||
{
|
|
||||||
return new SearchBuilder( $app['config'] );
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->mergeConfigFrom(
|
|
||||||
__DIR__ . '/../../config/config.php', 'searchy'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function boot()
|
|
||||||
{
|
|
||||||
$this->publishes([
|
|
||||||
__DIR__.'/../../config/config.php' => config_path('searchy.php'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the services provided by the provider.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function provides()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
'default' => 'fuzzy',
|
|
||||||
|
|
||||||
'fieldName' => 'relevance',
|
|
||||||
|
|
||||||
'drivers' => [
|
|
||||||
|
|
||||||
'fuzzy' => [
|
|
||||||
'class' => 'TomLingham\Searchy\SearchDrivers\FuzzySearchDriver'
|
|
||||||
],
|
|
||||||
|
|
||||||
'simple' => [
|
|
||||||
'class' => 'TomLingham\Searchy\SearchDrivers\SimpleSearchDriver'
|
|
||||||
],
|
|
||||||
|
|
||||||
'levenshtein' => [
|
|
||||||
'class' => 'TomLingham\Searchy\SearchDrivers\LevenshteinSearchDriver'
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
|
Reference in New Issue
Block a user