all(); // 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 ( $model->getSearchable() 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; } }