Laravel Cross Eloquent Search

repository·master·Indexed 22 days ago

https://github.com/protonemedia/laravel-cross-eloquent-search

A Laravel package for unified searching across multiple Eloquent models. It provides cross-model pagination, sorting, and full-text search support for MySQL 8.0+, PostgreSQL 12+, and SQLite 3.8+. Key features include searching through nested relationships, custom query constraints, relevance-based ordering, and flexible wildcard configuration. Requires PHP 8.2+ and Laravel 11.0+.

Tokens
5.6K
Snippets
23
Records
29
Agent score
77%

What's inside laravel-cross-eloquent-search

  1. Overview of Laravel Cross Eloquent Search features

    master

    This package provides a unified way to search through multiple Eloquent models simultaneously. Key capabilities include:

    • Multi-model Search: Search through one or more Eloquent models.
    • Pagination: Supports cross-model pagination.
    • Column Flexibility: Search through single or multiple columns, including nested relationships.
    • Full-Text Search: Supports Full-Text Search across models and relationships. The package automatically selects the correct strategy based on your database (MySQL native indexes, PostgreSQL tsquery with pg_trgm, or SQLite LIKE).
    • Sorting & Ordering: Order results by cross-model columns or by relevance.
    • Query Customization: Use Eloquent constraints, scoped queries, and eager load relationships for each model.
    • Database Support: Works with MySQL, PostgreSQL, and SQLite with zero third-party dependencies.
  2. Identify model types in results

    master

    By default, search results do not include the model type. To include it, call includeModelType(). This adds a type key to the result data.

    • Customizing the key: Pass a string to includeModelType('my_key') to change the key name.
    • Customizing the value: To change the value returned (e.g., from Post to article), add a public function searchType() method to your Eloquent model.
    // In your Model
    class Video extends Model
    {
        public function searchType()
        {
            return 'awesome_video';
        }
    }
    
    // In your Search
    Search::add(Post::class, 'title')
        ->includeModelType()
        ->search('foo');
  3. Configure Wildcards and Exact Match

    master

    By default, the package splits search terms and appends a % wildcard to each keyword for partial matching (e.g., apple becomes apple%).

    • beginWithWildcard(): Adds a wildcard to the start of terms (e.g., %apple%).
    • endWithWildcard(bool $value): Controls whether a wildcard is appended to the end. Call endWithWildcard(false) to disable trailing wildcards.
    • exactMatch(): Disables all wildcards and uses the = operator instead of LIKE for strict equality.
    // Partial matching with both sides
    Search::add(Post::class, 'title')
        ->beginWithWildcard()
        ->search('os');
    
    // Exact matching
    Search::add(Post::class, 'title')
        ->exactMatch()
        ->search('Laravel');
  4. Configure search with conditional logic and tapping

    master

    You can use Search::new() to start a fresh instance, which is useful for method chaining and indentation.

    • Use when($condition, $callback) to conditionally add models to the searcher.
    • Use tap($callback) to access the searcher instance (e.g., for logging configuration) before executing the search.
    • Use includeModelType($customKey = 'type') to add a field to the results identifying the model type.
    // Conditional adding
    Search::new()
        ->when($user->isVerified(), fn($search) => $search->add(Post::class, 'title'))
        ->when($user->isAdmin(), fn($search) => $search->add(Video::class, 'title'))
        ->search('howto');
    
    // Tapping into the instance
    Search::add(Post::class, 'title')
        ->tap(function ($searcher) {
            Log::info('Search configuration', ['models' => $searcher->getModelsToSearchThrough()]);
        })
        ->search('laravel');
  5. Perform a basic cross-model search

    master

    To search across multiple Eloquent models, use the Search::add() method to specify the model class and the column to search. Finally, call search() with your query term. This returns an ext{Illuminate extbackslash Database extbackslash Eloquent extbackslash Collection} of results.

    By default, results are sorted in ascending order by the model's updated_at column (or the primary key if timestamps are not used).

    use ProtoneMedia\
    LaravelCrossEloquentSearch\\Search;
    
    $results = Search::add(Post::class, 'title')
        ->add(Video::class, 'title')
        ->search('howto');
  6. Upgrade from v2 to v3

    master

    If you are upgrading from version 2 to version 3, note the following breaking changes:

    • Method Rename: The get method is now search.
    • Method Removal: The addWhen method has been removed. Use the when method instead.
    • Default Sorting: Results are now sorted by the updated column by default (typically updated_at). If your models do not use timestamps, it will default to the primary key.
  7. Upgrade from v1 to v2

    master

    If you are upgrading from version 1 to version 2, note the following breaking changes:

    • Method Rename: The startWithWildcard method is now beginWithWildcard.
    • Order Column Logic: The default order column is now determined by the getUpdatedAtColumn method, rather than being hard-coded to updated_at.
    • Empty Search Handling: The allowEmptySearchQuery method and EmptySearchQueryException class have been removed. To get results without a search query, follow the getting results without searching pattern.
  8. Search with constraints and relationships

    master

    You can extend the search scope in several ways:

    • Scoped Queries: Instead of a class name, pass an Eloquent query builder instance to add() to apply constraints (e.g., Post::published()).
    • Multiple Columns: Pass an array of columns as the second argument to add().
    • Nested Relationships: Use dot notation (e.g., comments.body) to search through related models.
    • Eager Loading: Use with() on the searcher to eager load relationships for the results.
    • Full-Text Search: Use addFullText() to leverage database-native full-text capabilities. For relationships, pass an array where keys are relation names and values are arrays of columns.
    // Scoped queries
    Search::add(Post::published(), 'title')
        ->search('compile');
    
    // Multiple columns and relationships
    Search::add(Post::class, ['title', 'body'])
        ->add(Video::class, ['comments.body'])
        ->search('solution');
    
    // Full-text search
    Search::new()
        ->addFullText(Video::class, 'title', ['mode' => 'boolean'])
        ->addFullText(Page::class, [
            'posts' => ['title', 'body'],
        ])
        ->search('framework -css');
  9. Sort search results

    master

    You can customize the sort order of the returned collection:

    • By Column: Pass a column name as the third argument to add(). Use orderByDesc() for descending order.
    • By Relevance: Use orderByRelevance() to sort by the number of occurrences of the search terms (not supported for nested relationship searches).
    • By Model Type: Use orderByModel([ClassA::class, ClassB::class]) to define a specific priority for different models in the results.
    // Sort by custom column
    Search::add(Post::class, 'title', 'published_at')
        ->orderByDesc()
        ->search('learn');
    
    // Sort by relevance
    Search::add(Post::class, 'title')
        ->beginWithWildcard()
        ->orderByRelevance()
        ->search('Apple iPad');
    
    // Sort by model priority
    Search::new()
        ->add(Comment::class, 'body')
        ->add(Post::class, 'title')
        ->orderByModel([
            Post::class, Video::class, Comment::class,
        ])
        ->search('Artisan School');
  10. Paginate search results

    master

    To avoid loading massive result sets, use pagination. The returned object will be an instance of Laravel's paginator.

    • paginate($perPage, $pageName, $page): Standard length-aware pagination.
    • simplePaginate($perPage, $pageName, $page): Simple pagination (no total count).
    • withQueryString($parameters = null): Retains query string parameters in pagination links. If $parameters is provided, it uses those specific keys; otherwise, it uses the current request's query string.
    // Standard pagination with query string retention
    Search::add(Post::class, 'title')
        ->paginate(15)
        ->withQueryString()
        ->search('build');
    
    // Simple pagination
    Search::add(Post::class, 'title')
        ->simplePaginate(15)
        ->search('build');