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

Pagination Filters Bundle Laravel Package

ekrouzek/pagination-filters-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require ekrouzek/pagination-filters-bundle
    

    Enable the bundle in config/bundles.php if not using Symfony Flex:

    Ekrouzek\PaginationFiltersBundle\PaginationFiltersBundle::class => ['all' => true],
    
  2. First Use Case Annotate a controller method with @QueryParam for pagination, filtering, and sorting:

    use Ekrouzek\PaginationFiltersBundle\Attribute\QueryParam;
    
    #[QueryParam(name: "page", requirements: "\d+", default: 1)]
    #[QueryParam(name: "itemsPerPage", requirements: "\d+", default: 10)]
    public function getCourses(ParamFetcher $paramFetcher) {
        $page = $paramFetcher->get('page');
        $itemsPerPage = $paramFetcher->get('itemsPerPage');
        // Use $page and $itemsPerPage in your query logic
    }
    
  3. Key Files to Review

    • src/Attribute/QueryParam.php (for annotations)
    • src/ParamFetcher.php (for fetching validated params)
    • src/EventListener/ParamFetcherListener.php (for integration hooks)

Implementation Patterns

Core Workflow

  1. Annotate Controller Methods Define expected query parameters with @QueryParam:

    #[QueryParam(name: "filter[name]", default: "", description: "Filter by course name")]
    #[QueryParam(name: "sort[field]", default: "createdAt", description: "Sort field")]
    #[QueryParam(name: "sort[direction]", default: "desc", description: "Sort direction")]
    
  2. Inject ParamFetcher Fetch validated parameters in your method:

    public function getCourses(ParamFetcher $paramFetcher) {
        $filters = $paramFetcher->getAll(); // Get all filtered params
        $nameFilter = $paramFetcher->get('filter[name]');
        $sortField = $paramFetcher->get('sort[field]');
        $sortDirection = $paramFetcher->get('sort[direction]');
    }
    
  3. Apply Filters/Sorting to Queries Use fetched values to build dynamic queries (e.g., with Doctrine):

    $queryBuilder = $entityManager->getRepository(Course::class)->createQueryBuilder('c');
    if ($nameFilter) {
        $queryBuilder->andWhere('c.name LIKE :name')->setParameter('name', "%$nameFilter%");
    }
    $queryBuilder->orderBy("c.$sortField", $sortDirection);
    
  4. Pagination Logic Implement pagination (e.g., with KnpPaginator or manual offset/limit):

    $offset = ($page - 1) * $itemsPerPage;
    $queryBuilder->setFirstResult($offset)->setMaxResults($itemsPerPage);
    
  5. Return Filtered Response Serialize results with metadata (e.g., total items, current page):

    $results = $queryBuilder->getQuery()->getResult();
    return $this->json([
        'data' => $results,
        'pagination' => [
            'page' => $page,
            'itemsPerPage' => $itemsPerPage,
            'totalItems' => $totalItems, // From COUNT query
        ],
    ]);
    

Integration Tips

  • Symfony Serializer: Use @Groups with @QueryParam to document API contracts.
  • OpenAPI/Swagger: Annotate with @ApiProperty to auto-generate API docs:
    #[QueryParam(name: "filter[status]", default: "active")]
    #[ApiProperty(description: "Filter courses by status (active/inactive)")]
    
  • Validation: Combine with Symfony Validator for stricter rules:
    #[QueryParam(name: "page", requirements: "\d+", default: 1)]
    #[Assert\GreaterThanOrEqual(1)]
    
  • Event Listeners: Extend ParamFetcherListener to add custom logic (e.g., default filters):
    // src/EventListener/CustomParamFetcherListener.php
    public function onKernelRequest(RequestEvent $event) {
        $request = $event->getRequest();
        if ($request->get('_route') === 'course_list') {
            $request->query->set('filter[status]', 'active');
        }
    }
    
    Register in services.yaml:
    services:
        App\EventListener\CustomParamFetcherListener:
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    

Gotchas and Tips

Pitfalls

  1. Parameter Naming Conflicts

    • Avoid overlapping parameter names (e.g., filter[name] vs. name). Use nested keys (e.g., filter[name] vs. search[name]).
    • Fix: Use unique prefixes like filter_, search_, or sort_.
  2. Default Values Overridden

    • Default values in @QueryParam are set after validation. If requirements fail, defaults are ignored.
    • Fix: Validate defaults separately or use null + custom logic:
      $page = $paramFetcher->get('page', 1); // Fallback to 1 if null
      
  3. Case-Sensitive Sorting

    • Default sorting may not handle case sensitivity (e.g., ASC vs. asc).
    • Fix: Normalize input:
      $direction = strtoupper($paramFetcher->get('sort[direction]', 'DESC'));
      
  4. Missing ParamFetcher Injection

    • Forgetting to inject ParamFetcher will bypass all filtering logic.
    • Fix: Use PHPStan or IDE hints to catch missing dependencies.
  5. Performance with Complex Filters

    • Dynamic LIKE or IN clauses can bloat queries. Test with EXPLAIN in production.
    • Fix: Limit filter depth or use full-text search for large datasets.

Debugging Tips

  1. Log Raw Parameters Dump ParamFetcher output to debug:

    $this->logger->debug('Raw params:', $paramFetcher->getAll());
    
  2. Validate Annotations Ensure @QueryParam attributes are correctly placed on methods, not classes.

  3. Check Event Listeners If filters aren’t applied, verify ParamFetcherListener is registered in services.yaml:

    services:
        Ekrouzek\PaginationFiltersBundle\EventListener\ParamFetcherListener:
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    
  4. Symfony Profiler Use the Request tab in Symfony Profiler to inspect query parameters.


Extension Points

  1. Custom ParamFetcher Extend ParamFetcher to add logic (e.g., type casting):

    class CustomParamFetcher extends ParamFetcher {
        public function getInt(string $name, int $default = null): int {
            return (int) $this->get($name, $default);
        }
    }
    

    Override in services.yaml:

    services:
        App\Service\CustomParamFetcher:
            decorates: 'ekrouzek_pagination_filters.param_fetcher'
    
  2. Dynamic Filter Rules Use events to modify filter behavior:

    // src/Event/FilterEvent.php
    class FilterEvent {
        public function addRule(string $name, callable $rule) { ... }
    }
    

    Dispatch in ParamFetcherListener:

    $dispatcher->dispatch(new FilterEvent($paramFetcher));
    
  3. API Platform Integration Combine with @ApiFilter for automatic OpenAPI docs:

    use ApiPlatform\Metadata\Operation;
    use Ekrouzek\PaginationFiltersBundle\Attribute\QueryParam;
    
    #[Operation(
        queryParams: [
            new QueryParam(name: "page", requirements: "\d+"),
            new QueryParam(name: "filter[name]"),
        ]
    )]
    
  4. Caching Filtered Responses Cache responses with a key including filter params:

    $cacheKey = 'courses_' . md5(serialize($paramFetcher->getAll()));
    return $this->cache->get($cacheKey, function() use ($queryBuilder) {
        return $queryBuilder->getQuery()->getResult();
    });
    
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