Installation:
composer require sofa/eloquence-base
Publish the config (optional):
php artisan vendor:publish --provider="Sofa\Eloquence\EloquenceServiceProvider"
First Use Case: Extend your Eloquent model to enable Searchable functionality:
use Sofa\Eloquence\Searchable\SearchableTrait;
class Post extends Model
{
use SearchableTrait;
protected $searchable = [
'columns' => ['title', 'body'], // Fields to search
'join' => ['author' => 'users'], // Optional: Related tables
];
}
Basic Search Query:
$results = Post::search('laravel')->get();
config/eloquence.php (for global configuration)app/Models/ (extend your models with traits)$posts = Post::search('query')->get();
$posts = Post::search('query', [
'weights' => ['title' => 2, 'body' => 1],
])->get();
$posts = Post::search('query', [
'join' => ['author' => 'users'],
])->get();
Combine with eloquence-mappable to transform search results:
use Sofa\Eloquence\Mappable\MappableTrait;
class PostResource extends JsonResource
{
public function toArray($request)
{
return $this->resource->map(['title', 'excerpt']);
}
}
Use eloquence-validable to validate search inputs:
use Sofa\Eloquence\Validable\ValidableTrait;
class PostRequest extends FormRequest
{
use ValidableTrait;
public function rules()
{
return ['query' => 'required|string|max:255'];
}
}
Pair with eloquence-metable for dynamic metadata:
use Sofa\Eloquence\Metable\MetableTrait;
class Post extends Model
{
use MetableTrait;
protected $meta = ['views', 'tags'];
}
Use eloquence-mutable to preprocess searchable fields:
use Sofa\Eloquence\Mutable\MutableTrait;
class Post extends Model
{
use MutableTrait;
protected $mutable = [
'search_title' => function ($value) {
return strtolower($value);
},
];
}
Database Indexing: Ensure searchable columns are indexed for performance:
Schema::table('posts', function (Blueprint $table) {
$table->fullText('title', 'body');
});
Caching Search Results: Cache frequent searches (e.g., autocomplete):
$cacheKey = 'posts_search_' . md5($query);
$results = Cache::remember($cacheKey, now()->addHours(1), function () use ($query) {
return Post::search($query)->get();
});
Scopes for Reusability: Create custom scopes in your model:
public function scopePublished($query)
{
return $query->where('published_at', '<=', now());
}
// Usage:
Post::search('query')->published()->get();
Testing Search Logic: Use Laravel’s testing helpers:
public function test_search_returns_results()
{
$post = Post::factory()->create(['title' => 'Test Post']);
$results = Post::search('test')->get();
$this->assertCount(1, $results);
}
Full-Text Search Limitations:
FULLTEXT index requires a minimum column length (e.g., MIN_LENGTH=4).ft_min_word_len in my.cnf or ensure search terms meet the threshold.Case Sensitivity:
FULLTEXT search is case-insensitive, but other databases (e.g., PostgreSQL) may vary.MATCH ... AGAINST ('query' IN NATURAL LANGUAGE MODE) for better control.Performance with Large Datasets:
tsvector).Join Clauses:
['author' => 'users']) may break queries.Post::search('query')->toSql().Trait Conflicts:
boot()).protected static function bootSearchable() for initialization.Log Generated Queries:
DB::enableQueryLog();
Post::search('query')->get();
dd(DB::getQueryLog());
Validate Searchable Configuration:
Ensure $searchable array is correctly defined in your model. Example error:
BadMethodCallException: Method search() does not exist.
Fix: Verify the trait is properly used and the config is published.
Handle Missing Full-Text Indexes:
If you get SQLSTATE[HY000]: General error: 1356 View ... references invalid table(s), ensure your columns are indexed.
Global Searchable Settings:
Override defaults in config/eloquence.php:
'searchable' => [
'default_columns' => ['title', 'content'],
'default_join' => [],
],
Dynamic Column Mapping: Use closures for dynamic searchable columns:
protected $searchable = [
'columns' => function () {
return ['title', 'body', 'tags.' . $this->category];
},
];
Excluding Models from Search:
Add a skipSearchable method to your model:
public function skipSearchable()
{
return true;
}
Custom Search Engines:
Extend the SearchableTrait to support Algolia/Elasticsearch:
class AlgoliaSearchableTrait
{
public function search($query, $options = [])
{
return Algolia::search($query, $options);
}
}
Add Search Metrics: Track search usage with middleware:
public function handle($request, Closure $next)
{
if ($request->has('search')) {
SearchLog::create(['query' => $request->search]);
}
return $next($request);
}
Searchable Events: Dispatch events for search actions:
use Sofa\Eloquence\Searchable\Events\Searching;
Searching::dispatch($this, $query, $options);
Hybrid Search (SQL + External): Combine database search with external APIs:
public function search($query, $options = [])
{
$dbResults = parent::search($query, $options);
$apiResults = ExternalSearch::search($query);
return $dbResults->merge($apiResults);
}
How can I help you explore Laravel packages today?