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

cta-k12/search-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require cta/search-bundle
    

    Add to config/bundles.php:

    CTA\SearchBundle\CTASearchBundle::class => ['all' => true],
    
  2. Annotate an Entity Use annotations (@Searchable, @Filterable, @Sortable) on Doctrine entities:

    use CTA\SearchBundle\Annotation\Searchable;
    use CTA\SearchBundle\Annotation\Filterable;
    use CTA\SearchBundle\Annotation\Sortable;
    
    /**
     * @Searchable({"name", "description"})
     * @Filterable({"status": {"values": ["active", "inactive"]}})
     * @Sortable({"name": "asc", "createdAt": "desc"})
     */
    class Product {}
    
  3. First Query Inject CTASearchManager and use it in a controller/service:

    use CTA\SearchBundle\Manager\CTASearchManager;
    
    public function search(CTASearchManager $searchManager) {
        $results = $searchManager->search(Product::class, [
            'query' => 'laptop',
            'filters' => ['status' => 'active'],
            'sort' => ['name' => 'asc']
        ]);
        return $this->render('products/index.html.twig', ['results' => $results]);
    }
    
  4. Twig Integration Loop through results in Twig:

    {% for product in results %}
        {{ product.name }} ({{ product.status }})
    {% endfor %}
    

Implementation Patterns

Common Workflows

  1. Basic Search

    $searchManager->search(Entity::class, ['query' => 'search term']);
    
    • Uses LIKE with %query% for each @Searchable field.
  2. Filtering

    $searchManager->search(Entity::class, [
        'filters' => [
            'status' => 'active',
            'category' => ['electronics', 'books']
        ]
    ]);
    
    • Supports exact matches for @Filterable fields (including arrays for multi-value fields).
  3. Sorting

    $searchManager->search(Entity::class, [
        'sort' => ['price' => 'desc', 'name' => 'asc']
    ]);
    
    • Respects @Sortable annotations; defaults to asc if direction omitted.
  4. Pagination

    $searchManager->search(Entity::class, [
        'query' => 'term',
        'page' => 2,
        'limit' => 20
    ]);
    
    • Returns a PaginatorInterface (Symfony’s PaginatorAdapter).
  5. Joined Entities Annotate relationships in entities:

    /**
     * @Searchable({"author.name"})
     */
    class Book {}
    
    • Query will search author.name via a join.

Integration Tips

  • Form Handling Bind form inputs to the search parameters:

    $query = $request->query->all();
    $results = $searchManager->search(Product::class, $query);
    
  • API Responses Serialize results with metadata:

    return $this->json([
        'data' => $results->getIterator(),
        'total' => $results->count(),
        'filters' => $request->query->get('filters')
    ]);
    
  • Dynamic Annotations Override annotations at runtime:

    $searchManager->search(Product::class, [
        'query' => 'term',
        'dynamicAnnotations' => [
            'Searchable' => ['name', 'description', 'sku']
        ]
    ]);
    
  • Caching Cache frequent queries (e.g., static filters):

    $cacheKey = md5(serialize($params));
    $results = $cache->get($cacheKey, function() use ($searchManager, $params) {
        return $searchManager->search(Entity::class, $params);
    });
    

Gotchas and Tips

Pitfalls

  1. Performance Caveats

    • No Full-Text Indexes: Uses LIKE queries, which are slow for large datasets (>10K records). Workaround: Add database-level full-text indexes for critical fields.
    • N+1 Queries: Joined @Searchable fields may trigger lazy-loading issues. Fix: Use fetch="EAGER" or DISTINCT in queries.
  2. Annotation Overrides

    • Runtime dynamicAnnotations replace static annotations, not merge. Tip: Use sparingly; prefer static annotations for clarity.
  3. Filter Values

    • @Filterable with values restricts input to predefined options. Gotcha: Invalid values throw exceptions. Validate client-side first.
  4. Sorting Ambiguity

    • Omitting sort direction defaults to asc, but this isn’t documented. Tip: Explicitly define directions in annotations for consistency.
  5. Case Sensitivity

    • LIKE searches are case-insensitive by default (MySQL/PostgreSQL). Note: SQLite may behave differently; test thoroughly.

Debugging

  1. Query Logging Enable Doctrine logging to inspect generated SQL:

    $config = Setup::createAnnotationMetadataConfiguration(
        [__DIR__.'/../src'],
        true,
        null,
        null,
        false
    );
    $config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
    
  2. Parameter Validation Validate input before passing to search():

    $validFilters = ['status' => ['active', 'inactive']];
    if (!in_array($request->query->get('status'), $validFilters['status'])) {
        throw new \InvalidArgumentException("Invalid status filter");
    }
    
  3. Entity Metadata Check if annotations are loaded:

    $metadata = $this->container->get('doctrine')->getManager()
        ->getMetadataFactory()->getMetadataFor(Entity::class);
    if (!$metadata->reflectionClass->hasAnnotation(Searchable::class)) {
        throw new \RuntimeException("Entity not searchable");
    }
    

Extension Points

  1. Custom Query Builders Extend CTASearchManager to add logic (e.g., Elasticsearch fallback):

    class CustomSearchManager extends CTASearchManager {
        public function search($entityClass, array $params) {
            if (strpos($params['query'], 'advanced:') === 0) {
                return $this->elasticSearchFallback($entityClass, $params);
            }
            return parent::search($entityClass, $params);
        }
    }
    
  2. Annotation Processors Override annotation handling in CTASearchExtension:

    $this->container->set('cta_search.extension', function() {
        return new class extends CTASearchExtension {
            public function loadAnnotations(ReflectionClass $class) {
                // Custom logic here
                return parent::loadAnnotations($class);
            }
        };
    });
    
  3. Result Transformers Modify results post-query:

    $searchManager->addPostProcessor(function($results) {
        foreach ($results as $entity) {
            $entity->setSearchScore($this->calculateScore($entity));
        }
        return $results;
    });
    
  4. Event Listeners Hook into search events (e.g., log searches):

    $eventDispatcher->addListener(CTASearchEvents::SEARCH, function(CTASearchEvent $event) {
        $this->logger->info('Search executed', [
            'entity' => $event->getEntityClass(),
            'query' => $event->getQuery(),
            'params' => $event->getParams()
        ]);
    });
    

Configuration Quirks

  • Default Values The bundle has no default config/packages/cta_search.yaml; all behavior is annotation-driven. Tip: Document your entity annotations centrally (e.g., in a SEARCH_RULES.md).

  • Bundle Order Register CTASearchBundle after DoctrineBundle in bundles.php to avoid metadata loading issues.

  • PHP 7.4+ Deprecations The bundle uses ReflectionClass::getAttributes() (PHP 8+), but runs on PHP 7. Use a polyfill if upgrading:

    if (!method_exists(\ReflectionClass::class, 'getAttributes')) {
        class_alias('Doctrine\Common\Annotations\Reflection\ReflectionClass', 'ReflectionClass');
    }
    
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai