Created
July 6, 2018 15:17
-
-
Save glmdev/60a01e6fe0fcf0eab16ae3ee016919ea to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Searches the collection of given $models for | |
* $string and returns the results. | |
* | |
* @param \Illuminate\Support\Collection $models | |
* @param $string | |
* @param $fields | |
* @return \Illuminate\Support\Collection | |
*/ | |
public static function search( $models, $string, $fields ){ | |
// define the model container in the correct scope | |
$model = null; | |
// sanitize and format the query | |
$query = formatQuery( $string ); | |
// initialize working arrays | |
$returns = []; | |
$toOpt = []; | |
$optHits = []; | |
$orgHits = []; | |
/** | |
* Count the number of times each word in the search | |
* string is found in the $item's searchable fields | |
* and push those to an array. | |
*/ | |
/* @var $item \Illuminate\Database\Eloquent\Model */ | |
foreach ( $models as $item ){ | |
$counts = []; | |
foreach ( $query as $word ){ | |
$wordcount = 0; | |
foreach ( $fields as $field ){ | |
$wordcount = $wordcount + substr_count( strtolower( $item->$field ), $word ); | |
} | |
$counts[ $word ] = $wordcount; | |
} | |
array_push( $toOpt, [ | |
'counts' => $counts, | |
'item' => $item | |
]); | |
}; | |
/** | |
* Convert the word found count for each individual | |
* word to a single unified hit count, adjusting | |
* for common words and letters and push to an | |
* array. | |
*/ | |
foreach ( $toOpt as $formattedItem ){ | |
$hits = 0; | |
foreach ( $formattedItem['counts'] as $word => $count ){ | |
if ( strlen( $word ) == 1 ){ | |
$hits += ($count*0.1); | |
} | |
elseif ( strlen( $word ) == 2 ){ | |
$hits += ($count*0.3); | |
} | |
else { | |
$hits += $count; | |
} | |
} | |
array_push( $optHits, [ | |
'hits' => $hits, | |
'item' => $formattedItem['item'] | |
]); | |
} | |
/** | |
* Sort the items in an array based on hit count. | |
* Where the key is the number of hits, push all | |
* items with that specific number of hits to | |
* the sub array. | |
*/ | |
foreach ( $optHits as $hitCountItem ){ | |
if ( isset( $orgHits[ (string) $hitCountItem['hits'] ] ) ){ | |
array_push( $orgHits[ (string) $hitCountItem['hits'] ], $hitCountItem['item'] ); | |
} | |
else { | |
$orgHits[ (string) $hitCountItem['hits'] ] = [ $hitCountItem['item'] ]; | |
} | |
} | |
/** | |
* Sort the array by the number of hits | |
* from low to high. So, the items with | |
* least hits to highest hits, still | |
* grouped. | |
*/ | |
ksort( $orgHits ); | |
/** | |
* Push each item to a single-dimensional array | |
* in order of least number of hits to highest | |
* number of hits. | |
*/ | |
foreach ( $orgHits as $hitCountGroup ){ | |
foreach ( $hitCountGroup as $item ){ | |
array_push( $returns, $item ); | |
} | |
} | |
/** | |
* Reverse the order of the array | |
* so that the first item has the | |
* greatest number of hits, and | |
* the last one has the least. | |
*/ | |
$returns = array_reverse( $returns ); | |
/** | |
* return the array in the form of a | |
* Laravel model collection | |
*/ | |
return collect( $returns ); | |
} | |
/** | |
* Formats the string into an array of non-punctuated, | |
* non-duplicated, lowercase words. | |
* | |
* @param $string | |
* @return array | |
*/ | |
public static function formatQuery( $string ){ | |
$words = explode(' ', $string); | |
$return = []; | |
foreach ( $words as $word ){ | |
$word = preg_replace('/[^[:alpha:]]/', '', $word); // removes all punctuation | |
$word = strtolower( $word ); | |
if ( ! in_array( $word, $return ) ) { | |
array_push($return, $word); | |
} | |
} | |
return $return; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment