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

Explorer Laravel Package

jeroen-g/explorer

A Laravel Scout driver for Elasticsearch and OpenSearch. Index and search Eloquent models with configurable mappings, analyzers, and settings, plus support for queues, bulk indexing, and advanced queries—ideal for scalable full‑text search.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require jeroen-g/explorer
    

    Publish the config:

    php artisan vendor:publish --provider="JeroenG\Explorer\ExplorerServiceProvider" --tag="config"
    
  2. Configure Elasticsearch: Update config/explorer.php with your Elasticsearch connection details (e.g., Docker, cloud, or local instance). Example:

    'connections' => [
        'default' => [
            'hosts' => [
                'http://elasticsearch:9200' // Docker example
            ],
            'username' => env('ELASTIC_USERNAME'),
            'password' => env('ELASTIC_PASSWORD'),
        ],
    ],
    
  3. Define an Index: Create a model and define its Elasticsearch index in config/explorer.php:

    'indices' => [
        'posts' => [
            'settings' => [
                'number_of_shards' => 1,
                'number_of_replicas' => 0,
            ],
            'mappings' => [
                'properties' => [
                    'title' => ['type' => 'text'],
                    'content' => ['type' => 'text'],
                    'published_at' => ['type' => 'date'],
                ],
            ],
        ],
    ],
    
  4. Update Scout Configuration: In config/scout.php, set the driver to explorer:

    'driver' => env('SCOUT_DRIVER', 'explorer'),
    
  5. First Search: Use Scout’s search() method in your model:

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

    Or leverage Explorer’s advanced query builder:

    use JeroenG\Explorer\Query\Builder;
    
    $query = Builder::for(Post::class)
        ->where('title', 'like', 'laravel')
        ->orderBy('published_at', 'desc')
        ->paginate(10);
    
    $results = $query->get();
    
  6. Initialize Index: Run the index update command:

    php artisan scout:import "App\Models\Post"
    

    Or use the Explorer-specific command for more control:

    php artisan explorer:update-index posts
    

First Use Case: Server-Side DataTable

Define an Explorer class for a DataTable (e.g., PostExplorer):

use JeroenG\Explorer\Explorer;

class PostExplorer extends Explorer
{
    public function query()
    {
        return Post::query();
    }

    public function columns()
    {
        return [
            'id' => 'ID',
            'title' => 'Title',
            'published_at' => 'Published At',
        ];
    }

    public function filters()
    {
        return [
            'search' => ['type' => 'text', 'label' => 'Search'],
            'published_after' => ['type' => 'date', 'label' => 'Published After'],
        ];
    }

    public function applyFilters($query, $filters)
    {
        if (isset($filters['search'])) {
            $query->where('title', 'like', "%{$filters['search']}%");
        }

        if (isset($filters['published_after'])) {
            $query->where('published_at', '>=', $filters['published_after']);
        }
    }
}

Use it in a controller:

public function index(Request $request)
{
    $explorer = new PostExplorer();
    $results = $explorer->query()
        ->search($request->search)
        ->filter('published_after', $request->published_after)
        ->paginate($request->per_page);

    return response()->json($results);
}

Implementation Patterns

Query Building Workflows

  1. Basic Search:

    $results = Post::search('query')->get();
    

    Equivalent to:

    $results = Builder::for(Post::class)
        ->where('title', 'like', 'query')
        ->orWhere('content', 'like', 'query')
        ->get();
    
  2. Advanced Filtering: Use Explorer’s query builder for complex constraints:

    $query = Builder::for(Post::class)
        ->where('published_at', '>=', '2023-01-01')
        ->whereNotIn('author_id', [1, 2])
        ->where('tags', 'contains', 'laravel')
        ->orderBy('views', 'desc')
        ->paginate(20);
    
  3. Aggregations: Group results by a field (e.g., category):

    $results = Builder::for(Post::class)
        ->termsAggregation('categories', 'category')
        ->get();
    

    Output includes aggregated buckets:

    {
        "aggregations": {
            "categories": {
                "buckets": [
                    {"key": "laravel", "doc_count": 42},
                    {"key": "php", "doc_count": 25}
                ]
            }
        }
    }
    
  4. Nested Aggregations: For nested objects (e.g., comments on posts):

    $results = Builder::for(Post::class)
        ->nestedAggregation('comments', 'comments', [
            'terms' => ['field' => 'comments.author']
        ])
        ->get();
    
  5. Custom Sorting:

    $query = Builder::for(Post::class)
        ->orderBy('title', 'asc')
        ->orderBy('published_at', 'desc')
        ->setMissing('published_at', 'last'); // Handle missing fields
    

Integration Tips

  1. Hybrid Search (SQL + Elasticsearch): Use Scout’s search() for full-text queries, then join with SQL for exact matches:

    $posts = Post::where('author_id', auth()->id())
        ->where(function ($query) {
            $query->where('title', 'like', '%query%')
                  ->orWhere('content', 'like', '%query%');
        })
        ->orWhereHas('searchable', function ($query) {
            $query->where('body', 'like', '%query%');
        })
        ->get();
    
  2. Real-Time Updates: Use Scout’s model observers to sync changes to Elasticsearch:

    Post::observe(Scoutable::class);
    

    For bulk updates, queue the scout:import command:

    Post::chunk(100, function ($posts) {
        Post::updateScoutModels($posts);
    });
    
  3. Index Management:

    • Update Index: php artisan explorer:update-index posts
    • Delete Index: php artisan scout:delete-index posts
    • Reindex: php artisan scout:flush "App\Models\Post"
  4. Testing: Use the fake Elasticsearch responses:

    use JeroenG\Explorer\Testing\Fakes\FakeElasticsearch;
    
    public function test_search()
    {
        FakeElasticsearch::fake([
            'posts' => [
                ['title' => 'Test Post', 'content' => 'Lorem ipsum']
            ]
        ]);
    
        $results = Post::search('test')->get();
        $this->assertCount(1, $results);
    }
    
  5. Custom Mappings: Extend the default mappings in config/explorer.php:

    'indices' => [
        'posts' => [
            'mappings' => [
                'properties' => [
                    'title' => [
                        'type' => 'text',
                        'analyzer' => 'english',
                        'fields' => [
                            'raw' => ['type' => 'keyword']
                        ]
                    ],
                    'content' => ['type' => 'text'],
                    'views' => ['type' => 'integer'],
                ]
            ]
        ]
    ]
    
  6. Logging: Enable PSR-3 logging in config/explorer.php:

    'logging' => [
        'enabled' => true,
        'channel' => 'single',
        'level' => 'debug',
    ],
    

Explorer Classes for Reusability

Create dedicated Explorer classes for complex tables (e.g., admin panels):

class UserExplorer extends Explorer
{
    public function query()
    {
        return User::with(['roles', 'posts']);
    }

    public function columns()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'email' => 'Email',
            'roles' => 'Roles',
            'post_count' => 'Posts',
        ];
    }

    public function filters()
    {
        return [
            'search' => ['type' => 'text', 'label' => 'Search'],
            'role' => ['type' => 'select', 'label' => 'Role', 'options' => Role::pluck('name', 'id')],
            'active' => ['type' => 'boolean', 'label' => 'Active'],
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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony