I have been using scout for a while and I think it is a great piece of software. Initially I was playing around with my own seach trait, which did a little bit of trickery revolving around using a scopeSearch.
Because using a scoped function has some backend trickery, we can allow our functions to be "understood" by it's relationships.
For example:
// app/Traits/MySearch.php
// modified from https://www.twilio.com/en-us/blog/build-live-search-box-laravel-livewire-mysql
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait MySearch
{
private function buildWildCards(string $term): string
{
if ($term == "") {
return $term;
}
// Strip MySQL reserved symbols
$reservedSymbols = ['-', '+', '<', '>', '@', '(', ')', '~', '"'];
$term = str_replace($reservedSymbols, '', $term);
$words = explode(' ', $term);
foreach($words as $idx => $word) {
// Add operators so we can leverage the boolean mode of
// fulltext indices.
$words[$idx] = "+" . $word . "*";
}
$term = implode(' ', $words);
error_log($term);
return $term;
}
public function scopeSearch(Builder $query, string $term): Builder
{
$columns = implode(',', $this->searchable);
// Boolean mode allows us to match john* for words starting with john
// (https://dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html)
$query->whereRaw(
"MATCH ({$columns}) AGAINST (? IN BOOLEAN MODE)",
$this->buildWildCards($term)
);
return $query;
}
}
// app/Models/User.php
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use Notifiable;
use MySearch;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public function todos(): HasMany
{
return $this->hasMany(Todo::class);
}
/**
* Get the indexable data array for the model.
*
* @return array<string, mixed>
*/
public function toSearchableArray(): array
{
return [
'name' => $this->name,
'email' => $this->email
];
}
}
class Todo extends Model
{
use HasFactory;
use MySearch;
protected $fillable = [
"user_id",
"title",
"completed",
];
protected $casts = [
'completed' => 'boolean',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
#[SearchUsingFullText(['title'])]
public function toSearchableArray(): array
{
return [
'title' => $this->title,
];
}
}
This allows for some code to be written like this for example:
User::first()->todos()->search('some_query')
I found this to be a really powerful feature, that allows for much more flexible use of the search functionality.
I believe that this function could very easily be ported over to Laravel Scout. It should be as simple as adding a scopedSearch() to the main trait to implement this. One would not even need remove the original search function.
If there are no objections to this (for any technical reason/limitation), I would be glad to get a PR up.
Hi there,
I have been using scout for a while and I think it is a great piece of software. Initially I was playing around with my own seach trait, which did a little bit of trickery revolving around using a scopeSearch.
Because using a scoped function has some backend trickery, we can allow our functions to be "understood" by it's relationships.
For example:
This allows for some code to be written like this for example:
User::first()->todos()->search('some_query')
I found this to be a really powerful feature, that allows for much more flexible use of the search functionality.
I believe that this function could very easily be ported over to Laravel Scout. It should be as simple as adding a scopedSearch() to the main trait to implement this. One would not even need remove the original search function.
If there are no objections to this (for any technical reason/limitation), I would be glad to get a PR up.