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 Bundle Laravel Package

aternovtsii/search-bundle

Laravel search bundle that adds a reusable, configurable search layer for your app. Provides easy integration for searching across multiple models/resources with a simple API, sensible defaults, and room to extend matching, filtering, and result formatting.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install the Package Add the package via Composer in a Laravel 10+ project (Symfony 8 compatible):

    composer require aternovtsii/search-bundle
    

    Note: If using Laravel <10, pin Symfony dependencies to avoid conflicts (e.g., symfony/dependency-injection:^7.4).

  2. Configure the Bundle Publish the bundle’s configuration:

    php artisan vendor:publish --provider="ATernovtsii\SearchBundle\SearchBundle" --tag="config"
    

    Edit config/search.php to define:

    • enabled: true (default).
    • client: elasticsearch or opensearch.
    • index_name: Your Elastic/OpenSearch index name.
    • hosts: Array of search engine URLs (e.g., ['http://localhost:9200']).
    • default_locale: en (or your app’s default).
  3. Enable Search for an Entity Annotate a Doctrine entity (e.g., Post) with searchable fields:

    use ATernovtsii\SearchBundle\Annotation\Searchable;
    use ATernovtsii\SearchBundle\Annotation\Field;
    
    /**
     * @Searchable(index="posts")
     */
    class Post
    {
        /**
         * @Field(type="text")
         */
        private string $title;
    
        /**
         * @Field(type="date")
         */
        private \DateTimeInterface $publishedAt;
    
        /**
         * @Field(type="relation", targetEntity="User", field="username")
         */
        private User $author;
    }
    
  4. First Search Query Inject the SearchService into a controller or command:

    use ATernovtsii\SearchBundle\Service\SearchService;
    
    class PostSearchController extends Controller
    {
        public function __construct(private SearchService $searchService) {}
    
        public function index()
        {
            $results = $this->searchService->search('Post', 'query=laravel');
            return view('posts.index', compact('results'));
        }
    }
    
  5. Reindex Data Run the reindex command to populate your search engine:

    php artisan search:reindex Post
    

    Optional: Enable event-based reindexing in config/search.php:

    'auto_reindex' => true,
    

Implementation Patterns

Workflows

1. Search Integration in Controllers

Pattern: Use dependency injection for SearchService to decouple search logic from business logic.

class ProductController extends Controller
{
    public function search(Request $request, SearchService $search)
    {
        $query = $request->input('q');
        $results = $search->search('Product', [
            'query' => $query,
            'sort' => ['price' => 'asc'],
            'limit' => 20,
        ]);
        return response()->json($results);
    }
}

2. Dynamic Field Mapping

Pattern: Extend field types for custom logic (e.g., nested objects, computed fields).

use ATernovtsii\SearchBundle\Annotation\Field;
use ATernovtsii\SearchBundle\Field\FieldInterface;

class CustomField implements FieldInterface
{
    public function getType(): string { return 'custom'; }

    public function getValue($entity): mixed
    {
        return $entity->getComputedValue();
    }
}

/**
 * @Field(type="custom", customClass="App\Search\CustomField")
 */
private string $computedField;

3. Pagination with Search

Pattern: Leverage Laravel’s pagination helpers with search results.

use Illuminate\Pagination\LengthAwarePaginator;

$results = $this->searchService->search('Article', [
    'query' => 'tutorial',
    'from' => $request->input('page') * 10,
    'size' => 10,
]);

$paginated = new LengthAwarePaginator(
    $results['hits'],
    $results['total'],
    10,
    $request->input('page', 1),
    ['path' => LengthAwarePaginator::resolveCurrentPath()]
);

4. Event-Driven Reindexing

Pattern: Trigger reindexing on entity updates via Doctrine events.

// config/search.php
'auto_reindex' => [
    'enabled' => true,
    'events' => ['prePersist', 'preUpdate', 'preRemove'],
],

Manual override: Disable for specific entities:

/**
 * @Searchable(autoReindex=false)
 */
class Newsletter
{
    // ...
}

5. Multi-Locale Search

Pattern: Search across translated fields using the locale parameter.

$results = $search->search('Product', [
    'query' => 'shirt',
    'locale' => 'fr', // French translations
]);

Integration Tips

Laravel-Specific Adaptations

  1. Service Container Binding Override the default SearchService binding in AppServiceProvider:

    $this->app->bind(SearchService::class, function ($app) {
        return new CustomSearchService($app->get('search.client'));
    });
    
  2. Artisan Command Aliases Create Laravel-style commands for search management:

    php artisan search:reindex Post
    php artisan search:analyze Post
    
  3. Blade Directives Extend Blade to render search forms:

    // app/Providers/BladeServiceProvider.php
    Blade::directive('searchForm', function ($expression) {
        return "<?php echo ATernovtsii\SearchBundle\Blade::searchForm($expression); ?>";
    });
    

    Usage:

    {!! searchForm('Post', ['fields' => ['title', 'description']]) !!}
    
  4. API Resource Integration Format search results as API resources:

    use ATernovtsii\SearchBundle\Resource\SearchResource;
    
    return SearchResource::collection($results['hits']);
    
  5. Queue-Based Reindexing Offload reindexing to queues for large datasets:

    // config/search.php
    'queue_reindex' => true,
    
    // In a command:
    $this->searchService->queueReindex('Product');
    

Gotchas and Tips

Pitfalls

  1. Symfony Version Conflicts

    • Issue: Laravel 10 bundles Symfony 6.4, but the package requires Symfony 8.0+. Conflicts may arise in symfony/dependency-injection, http-client, or event-dispatcher.
    • Fix: Pin Symfony dependencies in composer.json:
      "extra": {
          "laravel": {
              "dont-discover": ["vendor/aternovtsii/search-bundle"]
          }
      },
      "require": {
          "symfony/dependency-injection": "8.0.0",
          "symfony/http-client": "8.0.0"
      }
      
  2. Doctrine Event Overhead

    • Issue: Event-based reindexing (auto_reindex) triggers on every prePersist/preUpdate, which can slow down bulk operations.
    • Fix: Disable for high-frequency entities or use batch reindexing:
      $this->searchService->reindex('Product', ['batch_size' => 50]);
      
  3. Field Type Mismatches

    • Issue: Custom field types may not serialize correctly (e.g., FieldFloat for non-numeric values).
    • Fix: Validate field types in annotations:
      if (!is_numeric($entity->getPrice())) {
          throw new \InvalidArgumentException('Price must be numeric');
      }
      
  4. Index Template Conflicts

    • Issue: Manually created Elastic/OpenSearch indices may conflict with the bundle’s auto-generated templates.
    • Fix: Disable template generation in config/search.php:
      'template' => [
          'generate' => false,
      ],
      
      Then manually define the index mapping.
  5. Translation Loading Bug

    • Issue: Search results for translatable fields may return empty translations if $subSelects are misconfigured (fixed in v0.1.5).
    • Fix: Ensure default_locale is set and verify the locale parameter in queries.
  6. Max Result Window

    • Issue: Large datasets may hit Elastic/OpenSearch’s max_result_window limit (default: 10,000).
    • Fix: Configure in config/search.php:
      'client' => [
          'max_result_window' => 5000
      
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui