Installation:
composer require konekt/search
Publish the config file:
php artisan vendor:publish --provider="Konekt\Search\SearchServiceProvider"
Define Searchable Models:
In your model, add the Searchable trait and configure searchable columns:
use Konekt\Search\Searchable;
class Product extends Model
{
use Searchable;
protected $searchable = [
'columns' => ['name', 'description', 'sku'],
'join' => ['categories' => 'category_id'],
];
First Search Query:
use Konekt\Search\Facades\Search;
$results = Search::query('search_term')
->models([Product::class, User::class])
->get();
config/search.php – Adjust default settings (e.g., searchable key, default columns).Konekt\Search\Facades\Search – Primary entry point for queries.Searchable – Core trait for defining searchable models.$results = Search::query('term')
->models([Product::class, User::class])
->paginate(10);
$results = Search::query('term')
->models([Product::class])
->scopes(['active' => true, 'category_id' => 5])
->get();
$results = Search::query('term')
->models([Product::class])
->with(['categories', 'reviews'])
->get();
Define in model:
protected $searchable = [
'columns' => ['name', 'description', 'sku'],
];
Query:
Search::query('term')->models([Product::class])->get();
$results = Search::query('term')
->models([Product::class])
->sortBy('name', 'asc')
->get();
Search::extend('dynamic_model', function ($query) {
return $query->where('dynamic_field', 'like', '%' . $query->search . '%');
});
Use the package in controllers to return JSON responses:
public function search(Request $request)
{
$results = Search::query($request->input('q'))
->models([Product::class, User::class])
->paginate(15);
return response()->json($results);
}
Pass results to a view:
return view('search.results', [
'results' => $searchResults,
]);
Mock searches in tests:
$mock = Mockery::mock('overload:' . Search::class);
$mock->shouldReceive('query')->andReturnSelf();
$mock->shouldReceive('models')->andReturnSelf();
$mock->shouldReceive('get')->andReturn(collect([$fakeProduct]));
Search::query('test')->models([Product::class])->get();
Case Sensitivity:
LIKE).ILIKE or custom collations for case-insensitive searches.SearchServiceProvider:
Search::extend('postgres_case_insensitive', function ($query) {
$query->whereRaw("LOWER(column) LIKE LOWER(?)", ['%' . $query->search . '%']);
});
Performance with Large Datasets:
MATCH ... AGAINST) is not supported by default. Use database-specific full-text indexes for better performance.ALTER TABLE products ADD FULLTEXT(name, description);
Relation Joins:
join key in $searchable only supports simple foreign key relations. Complex joins (e.g., polymorphic) require custom query building.Search::query('term')->models([Product::class])->apply(function ($query) {
$query->join('categories', 'products.category_id', '=', 'categories.id')
->where('categories.name', 'like', '%' . $query->search . '%');
});
Reserved Keywords:
order, group, or limit may conflict with SQL keywords.protected $searchable = [
'columns' => ['`order`', '`group`'],
];
Pagination Conflicts:
cursor or length-aware), ensure the package’s paginate() method aligns with your setup.Search::query('term')->paginate(10, ['*'], 'cursor');
Log Raw Queries:
Enable query logging in config/search.php:
'debug' => env('SEARCH_DEBUG', false),
Or dump the query builder:
$query = Search::query('term')->models([Product::class])->getQuery();
\Log::info($query->toSql(), $query->getBindings());
Validate Model Configuration:
Ensure $searchable is properly defined in each model. Use:
if (!method_exists(Product::class, 'searchable')) {
throw new \RuntimeException('Model Product is not searchable.');
}
Handle Empty Results: Check for empty results early to avoid N+1 queries:
if (Search::query('term')->models([Product::class])->doesntExist()) {
return response()->json(['message' => 'No results found.'], 404);
}
Custom Query Builders:
Override the default query logic in SearchServiceProvider:
Search::extend('custom', function ($query) {
return $query->where(function ($q) {
$q->where('name', 'like', '%' . $query->search . '%')
->orWhere('slug', $query->search);
});
});
Add Searchable Columns Dynamically:
Use model observers or accessors to modify $searchable at runtime:
class ProductObserver
{
public function retrieving($product)
{
if (auth()->check()) {
$product->searchable['columns'][] = 'secret_field';
}
}
}
Database-Specific Optimizations: Create a custom connection driver for advanced features:
Search::connection('pgsql')->query('term')->models([Product::class])->get();
Then define the connection in config/search.php:
'connections' => [
'pgsql' => [
'driver' => 'pgsql',
'query' => CustomPostgresQueryBuilder::class,
],
],
Laravel Scout Integration: Combine with Scout for real-time search:
use Konekt\Search\ScoutSearchable;
class Product extends Model
{
use Searchable, ScoutSearchable;
public function toSearchableArray()
{
return [
'name' => $this->name,
'description' => $this->description,
];
}
}
How can I help you explore Laravel packages today?