granedirval/data-dog-pager-bundle
A Symfony bundle that connects your PHP application to Datadog, adding pager-style alerting and monitoring hooks so teams can spot and respond to production issues faster. Simple configuration and service wiring for Symfony apps.
Before installing, confirm your project is actually a Symfony 2.x / PHP 5.x–7.x application. This bundle targets Symfony2 and Doctrine ORM and was not written for Symfony 3+, Flex, or modern PHP; if you're on Symfony 4/5/6 the composer constraint will almost certainly fail or drag in incompatible dependencies. The realistic "day one" audience is a legacy Symfony2 app that needs pagination on an admin or listing screen.
Minimal steps:
composer require granedirval/data-dog-pager-bundleapp/AppKernel.php:
new GraneDirval\DataDogPagerBundle\DataDogPagerBundle(),
Resources/config/services.xml (the bundle's DI file) to see the exact service alias before wiring anything.First use case — paginating a Doctrine query builder in a controller:
$qb = $em->getRepository('AppBundle:Product')->createQueryBuilder('p')
->where('p.active = :active')
->setParameter('active', true);
$pager = $this->get('data_dog_pager')->paginate($qb, $page, 20);
// or whatever the actual service id is per the repo README
Then hand $pager (or its items + pager metadata) to Twig. The bundle's core job is small: wrap a QueryBuilder, apply filters/sorters, run a count query, apply offset/limit, and expose page metadata. Get that loop working on one controller before touching filters or sorters.
Controller-driven pagination. Keep the pager invocation in the controller or a small application service — don't bury it in entities or repositories. Fetch the page from $request->query->getInt('page', 1), clamp the limit, and build the query builder immediately above the paginate() call so the query is easy to read and debug.
Filters. The bundle's differentiating feature is filters layered on top of a base query builder. The pattern that works best:
$request->query (not the session), so pagination URLs are shareable and back/forward works.HAVING, GROUP BY), verify the count query the bundle generates matches the data query.Sorters. Same rule: whitelist sortable columns and ASC/DESC, then apply the orderBy before pagination. A common pattern is a $sortable = ['createdAt', 'name', 'price'] array mapped to actual columns. Sort state must travel in the query string alongside the page number, otherwise clicking "page 2" silently drops the sort.
Twig rendering. Iterate the paged items for the table/list, then render pagination controls from the pager's metadata (current page, total pages, has-next/prev). If the bundle ships a template, extend or copy it rather than rebuilding — it will encode the expected metadata keys. If it doesn't, you'll render links like ?page={{ page }}&{{ app.request.query.all|url_encode }} and strip the page key, or build the URL with the existing query params merged.
Reusable list action. If you have several paginated screens, write one base controller method or trait that accepts a QueryBuilder, a filter map, and a sorter map, and returns the pager + filters + sort state. That keeps filter/sorter behavior consistent and makes the odd broken list easy to spot.
Integration tips.
GROUP BY, DISTINCT, or joins that fan out rows, a pager's count query over-counts and you get phantom pages or truncated results. Inspect the count query via the Doctrine logger; if it's wrong, the usual fix is to hand the pager a separate count QueryBuilder or de-duplicate with DISTINCT.leftJoin/addSelect (fetch joins) for relations you render, or you'll fire a query per row.page is propagated. Explicitly merge all active filter/sort query parameters into every generated pager URL.orderBy or field names. Map through a whitelist.$qb->getQuery()->getSQL()) right before paginate() and inspect the count query separately. If pages come back empty, check that offset/limit are applied after filters and sorts, and that the limit isn't being overwritten by a config default.How can I help you explore Laravel packages today?