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

Paginator Bundle Laravel Package

dwalczyk/paginator-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require dwalczyk/paginator-bundle
    

    Register the bundle in config/bundles.php:

    DWalczyk\Paginator\PaginatorBundle::class => ['all' => true]
    
  2. First Use Case: Inject PaginatorInterface into a controller/service and paginate a Doctrine QueryBuilder:

    use DWalczyk\Paginator\PaginatorInterface;
    
    #[Route('/users')]
    public function listUsers(PaginatorInterface $paginator)
    {
        $query = $this->getEntityManager()->createQueryBuilder()
            ->select('u')
            ->from(User::class, 'u');
    
        $result = $paginator->paginate($query, 1, 20);
        return $this->render('users/index.html.twig', ['users' => $result]);
    }
    
  3. Key Files to Review:

    • src/PaginatorBundle/Resources/config/services.yaml (default service config).
    • src/DataLoader/DoctrineDataLoader.php (default implementation for QueryBuilder).

Implementation Patterns

Core Workflows

  1. Doctrine QueryBuilder Integration:

    • Use PaginatorInterface::paginate() with a QueryBuilder instance for automatic pagination.
    • Example:
      $paginator->paginate($qb->getQuery(), $page, $limit);
      
    • Returns a PaginatorResultInterface with:
      • getItems(): Array of results for the current page.
      • getTotalCount(): Total items across all pages.
      • getCurrentPage(): Current page number.
      • getItemsPerPage(): Items per page.
  2. Custom Data Sources:

    • Implement DataLoaderInterface for non-Doctrine data (e.g., API responses, CSV files):
      class ApiDataLoader implements DataLoaderInterface {
          public function loadItems(mixed $apiUrl, int $offset, int $limit): array {
              return $this->httpClient->request('GET', $apiUrl . "?offset=$offset&limit=$limit")->toArray();
          }
          public function loadTotalCount(mixed $apiUrl): int {
              return $this->httpClient->request('GET', $apiUrl . '/count')->toArray()['total'];
          }
      }
      
    • Bind the service in config/services.yaml:
      services:
          DWalczyk\Paginator\DataLoaderInterface: '@App\Service\ApiDataLoader'
      
  3. Twig Integration:

    • Pass the PaginatorResultInterface to Twig templates for pagination links:
      {% for user in result.items %}
          {{ user.name }}
      {% endfor %}
      
      {% if result.hasPreviousPage %}
          <a href="{{ path('users', { page: result.previousPage }) }}">Previous</a>
      {% endif %}
      

Advanced Patterns

  • Dynamic Limits: Use dependency injection to pass configurable limits:

    public function __construct(private int $defaultLimit) {}
    $paginator->paginate($query, $page, $this->defaultLimit);
    
  • Caching: Cache loadTotalCount() results if the dataset is static:

    public function loadTotalCount(mixed $target): int {
        return $this->cache->get('paginator:total:' . spl_object_hash($target), function() use ($target) {
            return $this->repository->count([]);
        });
    }
    
  • Symfony Messenger Integration: Defer heavy pagination to a background job:

    $this->messageBus->dispatch(new PaginateUsersMessage($query, $page, $limit));
    

Gotchas and Tips

Pitfalls

  1. Symfony 7.x Dependency:

    • The package only supports Symfony 7.x (minimum PHP 8.2). Ensure your project meets these requirements.
    • Error: Class 'Symfony\Component\HttpFoundation\Request' not found → Add symfony/http-foundation to composer.json.
  2. QueryBuilder Modifications:

    • The package does not modify the original QueryBuilder. If you need OFFSET/LIMIT, add them manually:
      $qb->setFirstResult(($page - 1) * $limit)
         ->setMaxResults($limit);
      
    • Gotcha: Forgetting to apply OFFSET/LIMIT will return all results for the first page.
  3. DataLoader Contracts:

    • loadItems() must return an array of items, not a Collection or ArrayObject.
    • loadTotalCount() must return an integer, not a Countable object.
  4. Circular References:

    • Avoid passing objects with circular references (e.g., entities with bidirectional relations) to loadItems(). Use DTOs or hydrate entities post-pagination.

Debugging Tips

  1. Verify DataLoader Binding:

    • Check if your custom DataLoaderInterface is correctly bound:
      php bin/console debug:container DWalczyk\Paginator\DataLoaderInterface
      
    • Fix: Clear cache (php bin/console cache:clear) if changes aren’t reflected.
  2. Pagination Off-by-One Errors:

    • If getCurrentPage() returns 0 instead of 1, ensure your DataLoaderInterface aligns with the package’s expectations. Override getCurrentPage() in a custom PaginatorResult if needed.
  3. Performance Issues:

    • For large datasets, avoid COUNT(*) in loadTotalCount(). Use a pre-aggregated count column or estimate:
      public function loadTotalCount(mixed $target): int {
          return $this->repository->getCountFromCache(); // Hypothetical cached count
      }
      

Extension Points

  1. Custom PaginatorResult: Extend PaginatorResult to add metadata:

    class ExtendedPaginatorResult extends PaginatorResult {
        public function hasMorePages(): bool {
            return $this->getCurrentPage() * $this->getItemsPerPage() < $this->getTotalCount();
        }
    }
    

    Bind it in services.yaml:

    DWalczyk\Paginator\PaginatorInterface:
        arguments:
            $resultClass: App\Service\ExtendedPaginatorResult
    
  2. Event Listeners: Hook into pagination events (e.g., PaginatorEvent::PRE_LOAD) via Symfony’s event dispatcher:

    $dispatcher->addListener(PaginatorEvent::PRE_LOAD, function(PaginatorEvent $event) {
        if ($event->getTarget() instanceof UserQuery) {
            $event->getQueryBuilder()->andWhere('u.active = 1');
        }
    });
    
  3. API Platform Integration: Use with API Platform’s Collection responses:

    #[ApiResource(paginationItemsPerPage: 20)]
    class User {}
    

    Override the paginator in config/packages/api_platform.yaml:

    api_platform:
        pagination:
            enabled: true
            client_items_per_page: true
            items_per_page_parameter_name: 'itemsPerPage'
            paginator: '@DWalczyk\Paginator\PaginatorInterface'
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity