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

Db Search Bundle Laravel Package

ayaou/db-search-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Bundle

    composer require ayaou/db-search-bundle
    

    Ensure your project meets the requirements: PHP 8.0+, Symfony 6.4+, Doctrine ORM.

  2. Generate and Run Migration

    php bin/console make:migration
    php bin/console doctrine:migrations:migrate
    

    This creates the db_search_index table.

  3. Configure Indexes Edit config/packages/db_search.yaml to define your indexes and entities:

    db_search:
      indexes:
        main:
          entities:
            App\Entity\Post:
              fields:
                - title
                - content
    
  4. Reindex Data

    php bin/console db-search:index:regenerate
    

    This populates the db_search_index table with searchable data.

  5. Search in Code Inject the Searcher service and call search():

    use Ayaou\DbSearchBundle\Searcher\Searcher;
    
    $results = $searcher->search('query');
    

First Use Case

Implement a basic search endpoint in a controller:

#[Route('/search', name: 'app_search')]
public function search(Request $request, Searcher $searcher): JsonResponse
{
    $query = $request->query->get('q');
    $results = $searcher->search($query);

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

Test with GET /search?q=test.


Implementation Patterns

Core Workflow

  1. Define Indexes Configure indexes in db_search.yaml to specify which entity fields to index. Example:

    db_search:
      indexes:
        products:
          entities:
            App\Entity\Product:
              fields:
                - name
                - description
                - tags
        users:
          entities:
            App\Entity\User:
              fields:
                - username
                - email
    
  2. Index Management

    • Initial Indexing: Run db-search:index:regenerate after adding new entities/fields.
    • Incremental Updates: Use events (e.g., postPersist, postUpdate) to update the index dynamically. Example:
      use Ayaou\DbSearchBundle\Event\IndexerEvent;
      use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
      
      #[AsEventListener(event: IndexerEvent::class, method: 'onIndexUpdate')]
      public function onIndexUpdate(IndexerEvent $event): void
      {
          $entity = $event->getEntity();
          if ($entity instanceof App\Entity\Post) {
              $event->markForIndexing();
          }
      }
      
  3. Search Patterns

    • Basic Search: Search across all indexes.
      $results = $searcher->search('query');
      
    • Scoped Search: Limit to a specific index or entity.
      // Search in 'products' index
      $results = $searcher->search('query', 'products');
      
      // Search for 'App\Entity\Product' only
      $results = $searcher->search('query', [], 'App\Entity\Product');
      
    • Raw Results: Get unprocessed database results.
      $rawResults = $searcher->searchRaw('query');
      
  4. Result Handling Process results in a controller or service:

    foreach ($results as $result) {
        $entity = $result['entity'];
        $score = $result['score']; // Relevance score (if available)
        $index = $result['index'];
    }
    

Integration Tips

  • Symfony Forms: Integrate search into forms for filtering:
    $form = $this->createFormBuilder()
        ->add('query', TextType::class, ['label' => 'Search'])
        ->getForm();
    
  • API Responses: Normalize search results for APIs:
    return $this->json([
        'data' => array_map(fn($r) => [
            'id' => $r['entity']->getId(),
            'title' => $r['entity']->getTitle(),
        ], $results),
    ]);
    
  • Caching: Cache frequent searches using Symfony’s cache system:
    $cacheKey = 'search:' . md5($query);
    $results = $cache->get($cacheKey, function() use ($searcher, $query) {
        return $searcher->search($query);
    });
    

Gotchas and Tips

Pitfalls

  1. Full-Text Search Limitations

    • MySQL’s FULLTEXT index requires a minimum word length (default: 4 characters). Configure in db_search.yaml:
      db_search:
        fulltext_min_word_length: 2 # Override default
      
    • PostgreSQL requires a custom extension (pg_trgm) for full-text search. The bundle does not support this out-of-the-box.
  2. Entity ID Requirements

    • Entities must implement getId(). Avoid using UUIDs or composite keys without proper configuration.
    • Workaround for non-standard IDs:
      // In your entity
      public function getId(): string
      {
          return $this->uuid->toString(); // For UUIDs
      }
      
  3. Indexing Performance

    • Large datasets may cause timeouts during db-search:index:regenerate. Split into batches:
      php bin/console db-search:index:regenerate --batch-size=1000
      
    • Avoid indexing TEXT or LONGTEXT fields directly; use summarized versions (e.g., title instead of content).
  4. Case Sensitivity

    • MySQL’s utf8mb4_unicode_ci collation is case-insensitive by default. For case-sensitive searches, use utf8mb4_bin (requires manual table alteration).
  5. Field Type Restrictions

    • Only string, int, float, and bool fields are indexed. Convert other types (e.g., DateTime) to strings:
      fields:
        - publishedAt # Assumes getPublishedAt() returns a DateTime
      
      Workaround in entity:
      public function getPublishedAt(): string
      {
          return $this->publishedAt->format('Y-m-d');
      }
      

Debugging

  1. Check Index Contents Inspect the db_search_index table directly:

    SELECT * FROM db_search_index WHERE indexed_text LIKE '%query%';
    

    Or use the searchRaw method:

    $raw = $searcher->searchRaw('query');
    dd($raw); // Debug raw results
    
  2. Verify Indexing Ensure entities are being indexed by checking the entity_class and entity_id columns. If missing, confirm:

    • The entity is listed in db_search.yaml.
    • The getId() method exists.
    • No exceptions during db-search:index:regenerate.
  3. Query Logs Enable Doctrine query logging to debug search queries:

    // In config/services.yaml
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle:
        resources:
            - "%kernel.project_dir%/config/doctrine.yaml"
        parameters:
            doctrine.dbal.logger: true
    

Extension Points

  1. Custom Indexers Extend the indexer to support custom logic. Implement Ayaou\DbSearchBundle\Indexer\IndexerInterface:

    use Ayaou\DbSearchBundle\Indexer\IndexerInterface;
    
    class CustomIndexer implements IndexerInterface
    {
        public function indexEntity(object $entity, string $indexName): string
        {
            // Custom indexing logic
            return "custom indexed text";
        }
    }
    

    Register as a service:

    services:
        App\Indexer\CustomIndexer:
            tags: ['db_search.indexer']
    
  2. Search Result Transformers Modify how results are formatted. Create a service tagged as db_search.result_transformer:

    use Ayaou\DbSearchBundle\Transformer\ResultTransformerInterface;
    
    class CustomTransformer implements ResultTransformerInterface
    {
        public function transform(array $result): array
        {
            $result['custom_field'] = 'value';
            return $result;
        }
    }
    
  3. Event Listeners Listen to indexing events to trigger side effects:

    use Ayaou\DbSearchBundle\Event\IndexerEvent;
    use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
    
    #[AsEventListener(IndexerEvent::POST_INDEX)]
    public function onPostIndex(IndexerEvent $event): void
    {
        // Log or notify after indexing
    }
    

Configuration Quirks

  1. Index Naming Index names in db_search.yaml must match exactly when searching:
    indexes:
      main: # Must use 'main' in searcher->search($query,
    
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