Installation
composer require jedrzej/searchable
Add the trait to your Eloquent model:
use Jedrzej\Searchable\SearchableTrait;
class Post extends Model
{
use SearchableTrait;
public $searchable = ['title', 'content', 'status'];
}
First Use Case Trigger a search via a request with query parameters:
// Example request: /posts?search[title]=Laravel&search[status]=published
$posts = Post::search()->get();
The package automatically filters records based on the search[] parameters.
Where to Look First
getSearchableAttributes() or $searchable property.search().Define Searchable Fields
class Product extends Model
{
use SearchableTrait;
public $searchable = ['name', 'category_id', 'price'];
}
price for numeric ranges).Trigger Searches
$results = Product::search()->where('search[name]', 'like', '%Laptop%')->get();
// Request: /products?search[name]=Laptop&search[category_id]=5
$results = Product::search()->get();
$results = Product::search()->where('search[category_id]', 5)->get();
Integration with Controllers
public function index(Request $request)
{
$query = Product::search();
if ($request->has('search')) {
$query->whereSearch($request->search);
}
return $query->paginate(10);
}
Custom Search Logic
Override scopeSearch() in your model for complex rules:
public function scopeSearch($query)
{
if ($this->searchable && $search = request('search')) {
foreach ($this->searchable as $field) {
if (isset($search[$field])) {
$query->where($field, 'like', "%{$search[$field]}%");
}
}
}
return $query;
}
Combining with Other Traits
Use with Sortable or Withable for full CRUD functionality:
use Jedrzej\Sortable\SortableTrait;
use Jedrzej\Withable\WithableTrait;
class Post extends Model
{
use SearchableTrait, SortableTrait, WithableTrait;
public $searchable = ['title', 'content'];
public $sortable = ['created_at', 'title'];
public $withable = ['author'];
}
API Resource Filtering
public function index()
{
return ProductResource::collection(
Product::search()->whereSearch(request('search'))->paginate()
);
}
Case Sensitivity
like queries are case-insensitive in MySQL but may vary by database. Use LOWER() for consistency:
$query->whereRaw('LOWER(field) LIKE LOWER(?)', ["%{$value}%"]);
Performance with Large Datasets
FULLTEXT indexes.select() to limit columns:
Product::search()->select(['id', 'name', 'price'])->get();
Request Parameter Naming
search[] parameters by default. Customize via:
public function getSearchInputName()
{
return 'filter'; // Now uses `filter[]` instead of `search[]`
}
Boolean Fields
1/0 for boolean fields (e.g., ?search[is_active]=1).Reserved Words
or, and, or where may cause SQL errors. Rename or alias them:
$query->where('is_active', 1)->getSearchResults();
Log Queries Enable Laravel’s query logging to inspect generated SQL:
DB::enableQueryLog();
Product::search()->whereSearch(request('search'))->get();
dd(DB::getQueryLog());
Validate Input Sanitize search inputs to prevent SQL injection:
$safeInput = strtolower(trim(request('search[name]'))); // Example
Test Edge Cases
?search[]=).' or ").id vs. price).Custom Operators
Extend the trait to support custom operators (e.g., >=, <=):
public function scopeWhereSearchGreaterThan($query, $field, $value)
{
return $query->where($field, '>=', $value);
}
Dynamic Field Whitelisting Use middleware to restrict searchable fields by user role:
public function handle($request, Closure $next)
{
if (auth()->user()->isAdmin()) {
$model->searchable = ['title', 'content', 'deleted_at'];
} else {
$model->searchable = ['title', 'published_at'];
}
return $next($request);
}
Localization Support multi-language searches by translating fields:
public function getSearchableAttributes()
{
return ['title_' . app()->getLocale()];
}
Caching Results Cache frequent searches (e.g., autocomplete):
$results = Cache::remember("search_{$query}", now()->addHours(1), function () {
return Product::search()->whereSearch($query)->limit(10)->get();
});
How can I help you explore Laravel packages today?