Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Search Laravel Package

konekt/search

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require konekt/search
    

    Publish the config file:

    php artisan vendor:publish --provider="Konekt\Search\SearchServiceProvider"
    
  2. 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'],
        ];
    
  3. First Search Query:

    use Konekt\Search\Facades\Search;
    
    $results = Search::query('search_term')
        ->models([Product::class, User::class])
        ->get();
    

Key Starting Points

  • Config File: config/search.php – Adjust default settings (e.g., searchable key, default columns).
  • Facade: Konekt\Search\Facades\Search – Primary entry point for queries.
  • Model Trait: Searchable – Core trait for defining searchable models.

Implementation Patterns

Core Workflows

1. Basic Search with Pagination

$results = Search::query('term')
    ->models([Product::class, User::class])
    ->paginate(10);

2. Scoped Search (Filtering)

$results = Search::query('term')
    ->models([Product::class])
    ->scopes(['active' => true, 'category_id' => 5])
    ->get();

3. Eager Loading Relations

$results = Search::query('term')
    ->models([Product::class])
    ->with(['categories', 'reviews'])
    ->get();

4. Multi-Column Search

Define in model:

protected $searchable = [
    'columns' => ['name', 'description', 'sku'],
];

Query:

Search::query('term')->models([Product::class])->get();

5. Sorting

$results = Search::query('term')
    ->models([Product::class])
    ->sortBy('name', 'asc')
    ->get();

6. Dynamic Model Registration

Search::extend('dynamic_model', function ($query) {
    return $query->where('dynamic_field', 'like', '%' . $query->search . '%');
});

Integration Tips

API Layer

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);
}

Blade Views

Pass results to a view:

return view('search.results', [
    'results' => $searchResults,
]);

Testing

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();

Gotchas and Tips

Pitfalls

  1. Case Sensitivity:

    • MySQL: Searches are case-insensitive by default (uses LIKE).
    • PostgreSQL/SQLite: May require ILIKE or custom collations for case-insensitive searches.
    • Fix: Override the query builder in SearchServiceProvider:
      Search::extend('postgres_case_insensitive', function ($query) {
          $query->whereRaw("LOWER(column) LIKE LOWER(?)", ['%' . $query->search . '%']);
      });
      
  2. Performance with Large Datasets:

    • Full-text search (MATCH ... AGAINST) is not supported by default. Use database-specific full-text indexes for better performance.
    • Tip: Add database indexes to frequently searched columns:
      ALTER TABLE products ADD FULLTEXT(name, description);
      
  3. Relation Joins:

    • The join key in $searchable only supports simple foreign key relations. Complex joins (e.g., polymorphic) require custom query building.
    • Workaround: Extend the trait or use raw queries:
      Search::query('term')->models([Product::class])->apply(function ($query) {
          $query->join('categories', 'products.category_id', '=', 'categories.id')
                ->where('categories.name', 'like', '%' . $query->search . '%');
      });
      
  4. Reserved Keywords:

    • Column names like order, group, or limit may conflict with SQL keywords.
    • Fix: Use table aliases or wrap columns in backticks:
      protected $searchable = [
          'columns' => ['`order`', '`group`'],
      ];
      
  5. Pagination Conflicts:

    • If using custom pagination (e.g., cursor or length-aware), ensure the package’s paginate() method aligns with your setup.
    • Tip: Pass pagination options:
      Search::query('term')->paginate(10, ['*'], 'cursor');
      

Debugging Tips

  1. 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());
    
  2. 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.');
    }
    
  3. 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);
    }
    

Extension Points

  1. 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);
        });
    });
    
  2. 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';
            }
        }
    }
    
  3. 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,
        ],
    ],
    
  4. 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,
            ];
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope