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

Laravel Searchable Laravel Package

mozex/laravel-searchable

Add a Searchable trait to any Eloquent model to search multiple columns and related data (relations, morphs, even cross-database) via a single ->search() call. Works with Laravel Scout and includes optional Filament table/global search integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require mozex/laravel-searchable
    

    No additional configuration or migrations are required.

  2. Basic Setup: Add the Searchable trait to your Eloquent model and define searchableColumns():

    use Mozex\Searchable\Searchable;
    
    class Comment extends Model
    {
        use Searchable;
    
        public function searchableColumns(): array
        {
            return ['body', 'author.name']; // Direct column + relation
        }
    
        public function author()
        {
            return $this->belongsTo(User::class);
        }
    }
    
  3. First Search:

    $results = Comment::search('laravel')->get();
    

    Or chain with other queries:

    Comment::query()->where('published', true)->search($request->q)->paginate();
    

Where to Look First

  • Documentation: mozex.dev/docs/laravel-searchable/v1
  • Source: Mozex\Searchable\Searchable trait and Mozex\Searchable\SearchableScope class
  • Filament Integration: If using Filament, check Mozex\Searchable\Filament\SearchableGlobalSearchProvider

Implementation Patterns

Core Workflow

  1. Define Searchable Columns: Use dot notation for relations (author.name) and colon notation for morphs (commentable:post.title).

    public function searchableColumns(): array
    {
        return [
            'title',                     // Direct column
            'author.name',               // BelongsTo relation
            'tags.name',                 // HasMany relation
            'commentable:post.title',    // Morph relation
        ];
    }
    
  2. Search Execution:

    // Basic search
    Model::search('term')->get();
    
    // With query constraints
    Model::query()->where('status', 'active')->search('term')->get();
    
    // Dynamic column filtering
    Model::search('term', in: ['title', 'body'])->get();
    
  3. Filament Integration:

    // Table column
    TextColumn::make('title')->advancedSearchable()->sortable();
    
    // Global search (register provider in panel)
    $panel->globalSearch(SearchableGlobalSearchProvider::class);
    

Advanced Patterns

  1. Cross-Database Relations: Automatically handled for BelongsTo/MorphTo relations. Adjust externalLimit if needed:

    Model::search('term', externalLimit: 200)->get();
    
  2. Dynamic Column Control: Override searchable columns per query:

    Model::search('term', include: ['slug'], except: ['author.name'])->get();
    
  3. Conflict Resolution:

    • Scout Conflict: Alias the trait method:
      use Mozex\Searchable\Searchable as DatabaseSearchable;
      class Model extends BaseModel {
          use DatabaseSearchable { scopeSearch as scopeDatabaseSearch; }
      }
      
    • Builder Conflict: Override search() in the builder:
      class CustomBuilder extends Builder {
          public function search($term) {
              $this->getModel()->applySearch($this, $term);
              return $this;
          }
      }
      
  4. Performance Optimization:

    • Use pg_trgm GIN indexes on PostgreSQL for LIKE '%term%' queries.
    • Filter by indexed columns (e.g., tenant_id) before applying search.

Gotchas and Tips

Pitfalls

  1. Case Sensitivity:

    • Defaults to case-insensitive (LIKE/ILIKE). For case-sensitive searches, modify column collation in the database.
    • SQLite only matches ASCII case-insensitively (e.g., Cafécafé).
  2. Wildcard Performance:

    • Leading wildcards (%term%) prevent index usage. For large tables, consider:
      • Laravel Scout (Meilisearch/Typesense).
      • Full-text indexes (PostgreSQL tsvector, MySQL FULLTEXT).
  3. Cross-Database Limits:

    • External relation searches cap at 50 IDs by default (adjust with externalLimit).
    • Large IN clauses may cause memory issues or timeouts.
  4. Method Conflicts:

    • If scopeSearch is already defined (e.g., in a parent model), alias the trait method:
      use Mozex\Searchable\Searchable { scopeSearch as scopeDbSearch; }
      
    • For custom builders (e.g., Corcel), override search() to delegate to applySearch().
  5. Filament Global Search:

    • Mandatory: Each resource must define getGloballySearchableAttributes().
    • Fallback: Models without Searchable trait use Filament’s default global search.

Debugging Tips

  1. Query Inspection: Use Laravel’s query logging or toSql() to verify generated SQL:

    $query = Model::search('term')->toSql();
    
  2. Column Validation: Ensure relation paths in searchableColumns() match your model’s actual relations (e.g., author.name requires author() method).

  3. External Relation Debugging:

    • Check externalLimit if searches return incomplete results.
    • Verify the external connection’s credentials and table structure.
  4. Performance Profiling:

    • Use DB::enableQueryLog() to analyze subqueries for morph/relation searches.
    • Test with EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN (MySQL) to identify bottlenecks.

Extension Points

  1. Custom Search Logic: Override applySearch() in your model for bespoke behavior:

    public function applySearch(Builder $query, $term, array $options = [])
    {
        // Custom logic here
        parent::applySearch($query, $term, $options);
    }
    
  2. Filament Macro Customization: Extend advancedSearchable() for additional parameters:

    TextColumn::macro('customSearchable', function () {
        return $this->advancedSearchable(method: 'databaseSearch', include: ['slug']);
    });
    
  3. Scout Hybrid Search: Combine database and Scout searches:

    $databaseResults = Model::databaseSearch('term')->get();
    $scoutResults = Model::search('term')->get();
    $merged = $databaseResults->merge($scoutResults);
    
  4. Dynamic Column Loading: Use Laravel’s with() to eager-load relations for search:

    Model::with(['author', 'tags'])->search('term')->get();
    
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.
craftcms/url-validator
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony