igraal/stats-table-bundle
igraal/stats-table-bundle is a Symfony bundle for integrating statistics tables into your application. It provides ready-made table structures and configuration to help you display aggregated stats with minimal setup.
igraal/stats-table-bundle is a thin Symfony integration wrapper around the stats-table library, so the first move is to confirm what the library provides before wiring the bundle.
Install both layers explicitly. The bundle won't necessarily pull in the core library as a hard dependency in a way that matches your Symfony version:
composer require igraal/stats-table-bundle
Check composer show igraal/stats-table-bundle and read its composer.json — note the required symfony/* versions and whether stats-table is a require or a suggest. If it's suggested, add it yourself.
Register the bundle. In modern Symfony (Flex) this is usually automatic via config/bundles.php; for older skeletons add new Igraal\StatsTableBundle\StatsTableBundle(), to AppKernel. Run bin/console debug:container --tag=bundle or just bin/console about to confirm it loaded.
Read the source, not just the README. For a zero-star, zero-dependent bundle, the DependencyInjection/ directory is the real documentation:
*Extension.php tells you the config tree (config/packages/stats_table.yaml keys).Resources/config/services.xml (or services.yaml) tells you which service IDs exist and what they're aliased to.CompilerPass files reveal extension points you'd otherwise miss.First use case. The bundle almost certainly exposes the stats-table builder as a service. Pull it from the container in a controller and build a table:
public function report(StatsTableFactory $factory): Response
{
$table = $factory->create()
->setColumns(['user', 'orders', 'revenue'])
->addRow(['alice', 12, '340.00'])
->addRow(['bob', 8, '210.50']);
return $this->render('report.html.twig', ['table' => $table]);
}
If the bundle ships a Twig extension (look for Twig/ in src/), you'll instead render with something like {{ stats_table(table) }} — check debug:twig for the actual filter/function names.
Look first at: README.md, then DependencyInjection/Configuration.php, then any tests/ directory. Tests are the most reliable usage examples for an under-documented package.
Service injection over container access. Once you know the service ID (e.g. igraal_stats_table.factory), alias it in your own services.yaml:
services:
Igraal\StatsTableBundle\StatsTableFactory: '@igraal_stats_table.factory'
This keeps your code decoupled and makes it trivial to swap in a test double.
Build tables in a dedicated presenter/service, not controllers. Stats tables tend to be reused (dashboard, export, email digest). Create App\Stats\OrderStatsTable that takes the factory plus repositories and returns a configured table. Controllers then just call $this->orderStatsTable->build($range).
Compose via the builder, not array literals. If the underlying library follows the usual fluent builder style, keep chains small and name intermediate variables. Long ->addRow(...)->addRow(...) chains become unreadable at ~10 rows; loop over query results instead:
foreach ($repository->findTotalsByRange($from, $to) as $row) {
$table->addRow([$row['label'], $row['count'], $row['amount']]);
}
Integrate with Symfony's serializer/normalizer for exports. A stats table is just tabular data; if you also need CSV or JSON, hydrate a DTO collection first and feed both the table builder and the serializer from the same source. Avoid building the table directly from a QueryBuilder result — you lose the ability to reuse it.
Configuration over constructor args. Anything the bundle exposes under stats_table: (default column types, formatters, decimal separators) should live in config/packages/stats_table.yaml and be overridable per-environment. Don't hardcode formatting in PHP if the bundle already offers a formatter service.
Twig integration. If a Twig extension is present, prefer rendering through it for consistency of markup; only fall back to manual {% for %} loops if you need custom cell markup. Register the extension's runtime (not the extension itself) as lazy if it does heavy work.
Testing. Unit-test table-building logic by asserting on the data structure (getRows(), getColumns()) rather than rendered HTML — HTML assertions break on cosmetic changes. Use Symfony's KernelTestCase to smoke-test that the bundle's services wire up in the test kernel.
symfony/dependency-injection upper bound. Pin the Symfony constraint or you'll get resolver conflicts on upgrade.config/routes recipe or auto-registration; you may need to manually add the bundle and, if it exposes routes, import them with stats_table: { resource: "@StatsTableBundle/Resources/config/routing.xml" }.Configuration.php may define keys that aren't actually used by the extension, or vice versa. After setting stats_table: config, run bin/console debug:config stats_table to confirm the values were accepted and bin/console config:dump-reference stats_table to see what's actually supported.services.xml was written for Symfony 2/3, services won't be autowired or public. You'll see Cannot autowire service ... or "service not found" errors. Workaround: define an alias to the concrete service ID in your app config, or use a compiler pass to make it public in dev.Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface usage, getDefinition() in Bundle subclasses, or Resources/config paths — the presence of these suggests it targets an old Symfony version and may emit deprecations under 6.x/7.x.stats_table could collide with your own; check debug:twig | grep -i stats and rename locally if needed.tests/ directory, write a throwaway command (bin/console app:stats-table-demo) to exercise the builder end-to-end before committing to it in a real feature.stats-table builder yourself from a Symfony service definition. If the bundle causes version friction, that's the pragmatic exit.How can I help you explore Laravel packages today?