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

Simple Pagination Bundle Laravel Package

ashleydawson/simple-pagination-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require ashleydawson/simple-pagination-bundle
    
  2. Register the bundle in config/bundles.php (Symfony 4/5):

    return [
        // ...
        AshleyDawson\SimplePaginationBundle\AshleyDawsonSimplePaginationBundle::class => ['all' => true],
    ];
    

    (For Symfony 3, add to AppKernel.php as shown in the README.)

  3. Basic usage with an array:

    $paginator = $this->container->get('ashley_dawson_simple_pagination.paginator');
    $items = ['item1', 'item2', ..., 'item12'];
    
    $paginator
        ->setItemTotalCallback(fn() => count($items))
        ->setSliceCallback(fn($offset, $length) => array_slice($items, $offset, $length));
    
    $pagination = $paginator->paginate($request->query->getInt('page', 1));
    

    Render in Twig:

    {% for item in pagination.items %}{{ item }}{% endfor %}
    {{ simple_pagination_render(pagination, '_route_name', 'page', app.request.query.all) }}
    

Implementation Patterns

1. Doctrine Integration (Most Common Use Case)

  • Count Query: Use a separate query for COUNT(*) in setItemTotalCallback.
  • Slice Query: Reuse the base query in setSliceCallback with setFirstResult() and setMaxResults().
  • Example:
    $query = $entityManager->createQueryBuilder()
        ->select('u')
        ->from('App\Entity\User', 'u')
        ->where('u.active = :active')
        ->setParameter('active', true);
    
    $paginator
        ->setItemTotalCallback(fn() => $query->select('COUNT(u.id)')->getQuery()->getSingleScalarResult())
        ->setSliceCallback(fn($offset, $length) => $query->setFirstResult($offset)->setMaxResults($length)->getQuery()->getResult());
    

2. Dynamic Configuration

  • Override defaults per request:
    $paginator
        ->setItemsPerPage($request->query->getInt('per_page', 10))
        ->setPagesInRange($request->query->getInt('pages_in_range', 5));
    
  • Global defaults in config/packages/ashley_dawson_simple_pagination.yaml:
    ashley_dawson_simple_pagination:
        defaults:
            items_per_page: 20
            pages_in_range: 3
    

3. Custom Services

  • Define reusable paginators in config/services.yaml:
    services:
        App\Service\AdminPaginator:
            class: AshleyDawson\SimplePagination\Paginator
            calls:
                - [setItemsPerPage, [50]]
                - [setPagesInRange, [10]]
    
  • Inject via constructor or autowire:
    public function __construct(private Paginator $paginator) {}
    

4. Twig Rendering

  • Default template: Uses AshleyDawsonSimplePaginationBundle:Pagination:default.html.twig.
  • Override template:
    {{ simple_pagination_render(pagination, 'route_name', 'page', app.request.query.all, 'App:Pagination:custom.html.twig') }}
    
  • Customize links: Extend the template to modify HTML/URLs (e.g., add icons, active state styling).

5. API Responses

  • Return pagination metadata in JSON:
    return $this->json([
        'data' => $pagination->items,
        'meta' => [
            'total' => $pagination->totalItems,
            'page' => $pagination->currentPage,
            'last_page' => $pagination->lastPage,
        ],
    ]);
    

Gotchas and Tips

Pitfalls

  1. Query Reuse:

    • Issue: Forgetting to reset the query between setItemTotalCallback and setSliceCallback (e.g., COUNT vs. SELECT *).
    • Fix: Clone the query builder or use separate instances:
      $countQuery = clone $query->select('COUNT(u.id)');
      $sliceQuery = clone $query->select('u');
      
  2. Offset Calculation:

    • Issue: Off-by-one errors if items_per_page isn’t aligned with database limits (e.g., MySQL’s LIMIT).
    • Fix: Use ($page - 1) * $itemsPerPage explicitly in callbacks.
  3. Caching:

    • Issue: setItemTotalCallback may hit the database twice (once for count, once for slice) if not optimized.
    • Fix: Cache the total count in a service or use Doctrine’s getSingleScalarResult() efficiently.
  4. Symfony 5+ Deprecations:

    • Issue: get() method is deprecated in favor of getContainer()->get().
    • Fix: Use dependency injection or autowire: true in services.
  5. Twig Function Parameters:

    • Issue: simple_pagination_render fails if routeName doesn’t exist or pageParameterName is misconfigured.
    • Fix: Validate routes and parameters:
      {% if app.request.query.get('page') is not null %}
          {{ simple_pagination_render(pagination, 'valid_route', 'page') }}
      {% endif %}
      

Debugging Tips

  • Log Callbacks: Add debug logs to verify setItemTotalCallback and setSliceCallback are called correctly:
    ->setItemTotalCallback(fn() => (int)($count = $query->getQuery()->getSingleScalarResult()) && $this->logger->debug('Total items:', ['count' => $count]))
    
  • Check Pagination Object: Dump the $pagination object to inspect:
    $this->logger->debug('Pagination data:', [
        'currentPage' => $pagination->currentPage,
        'totalItems' => $pagination->totalItems,
        'items' => count($pagination->items),
    ]);
    
  • Test Edge Cases: Verify behavior with:
    • Empty datasets (totalItems = 0).
    • Single-page results (totalItems <= itemsPerPage).
    • Large offsets (e.g., page 1000).

Extension Points

  1. Custom Pagination Logic:

    • Extend the Paginator class to add methods like paginateWithSort() or paginateWithFilters().
    • Example:
      class FilteredPaginator extends Paginator {
          public function paginateWithFilter($page, array $filters) {
              $this->setSliceCallback(fn($offset, $length) => $this->applyFilters($filters)->setFirstResult($offset)->setMaxResults($length)->getResult());
              return $this->paginate($page);
          }
      }
      
  2. Override Twig Templates:

    • Copy AshleyDawsonSimplePaginationBundle:Pagination:default.html.twig to templates/pagination/default.html.twig to customize pagination links.
    • Example modification: Add aria-current to active page links.
  3. Async Pagination:

    • Use Symfony’s Messenger component to defer heavy pagination tasks (e.g., for admin dashboards):
      $message = new PaginateUsersMessage($offset, $length);
      $this->messageBus->dispatch($message);
      // Return cached data while processing in background.
      
  4. Integration with API Platform:

    • Use the bundle’s Pagination object to generate HATEOAS links or JSON:API pagination headers:
      return new JsonResponse([
          'data' => $pagination->items,
          'links' => [
              'first' => $this->generateUrl('api_users', ['page' => 1]),
              'last' => $this->generateUrl('api_users', ['page' => $pagination->lastPage]),
          ],
      ]);
      

Performance Quirks

  • Database Load: Avoid COUNT(*) in setItemTotalCallback for large tables. Use:
    ->select('COUNT(u.id)')->where('u.deleted_at IS NULL')
    
  • Memory Usage: For very large datasets, stream results instead of loading all items into memory (e.g., with generators or cursor-based pagination).
  • Caching: Cache the totalItems count if it rarely changes:
    $cache = $this->container->get('cache.app');
    $cacheKey = 'user_count_active';
    $total = $cache->get($cacheKey, function() use ($query) {
        return $query->select('COUNT(u.id)')->getQuery()->getSingleScalarResult();
    });
    
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.
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
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager