tchoulom/view-counter-bundle
A Symfony bundle for counting and tracking page or entity views. It records each visit, stores statistics in Doctrine, and lets you display total view counts and popularity metrics with simple Twig helpers and configurable counters.
composer require tchoulom/view-counter-bundle
config/bundles.php if Symfony Flex does not do it automatically:
Tchoulom\ViewCounterBundle\TchoulomViewCounterBundle::class => ['all' => true],
DependencyInjection/Configuration.php — available config keysResources/config/services.yaml — service IDs and autowiring aliasesEntity/ — persistence model and required schemaTwig/ — helpers, if anyconfig/packages/ and run Doctrine migrations if the bundle ships its own entity/table.If the README is thin, treat Configuration.php and services.yaml as the source of truth. Confirm the supported Symfony and Doctrine versions before wiring it into a real project.
Explicit controller counting
Count only on the detail action, not on every request:
public function show(Article $article): Response
{
$this->viewCounter->count($article);
return $this->render('article/show.html.twig', [
'article' => $article,
'views' => $this->viewCounter->getCount($article),
]);
}
Use the exact service ID/class from your installed version; older examples often reference a service like tchoulom.view_counter.
Automatic counting with an event subscriber
For consistent behavior across many controllers, listen to kernel.controller or kernel.request, resolve the entity from route attributes, and count only for configured routes/entities. Keep an allowlist of routes to avoid counting admin, API, or asset requests.
Twig integration
If the bundle provides a Twig extension, use it for display. Otherwise, pass the count from the controller. Avoid calling the counter service directly from templates because it hides write side effects inside rendering.
Duplicate-view protection
Combine the bundle count with session, cookie, or cache checks:
Popular-content queries
If the bundle stores counts in a Doctrine entity/table, query it from a repository or custom service for “most viewed” lists. Join back to your domain entity and cache the result for listing pages.
Admin dashboards
Expose counts through a read-only service or repository. Keep dashboard queries separate from write-path counting so a slow report does not affect page rendering.
Asynchronous counting
For high-traffic pages, dispatch a Symfony Messenger message from the controller and let a handler call the counter service. This keeps the page response fast and makes retries/debugging easier.
Testing
In functional tests, disable automatic counting unless the test is specifically about views. Assert that one page view increments by one, and that repeat views follow your duplicate-protection rules.
Low adoption risk
This package has very few stars and zero known dependents. Audit the code before production use, pin the version in composer.json, and be prepared to fork or replace it. Check compatibility with your Symfony, Doctrine, and PHP versions.
Service visibility and autowiring
Older bundles often define private services without autowiring aliases. If injection fails, inspect services.yaml, use the explicit service ID, or add an alias in your app config.
Schema and migrations
The bundle may ship entity mappings but not migrations. Generate and review migrations yourself. Verify table names, indexes on the counted entity, and whether counts are stored on your entity or in a separate table.
Double counting
Browser prefetching, bots, refreshes, and AJAX partial renders can inflate counts. Count only on the final HTML detail response, exclude known bots, and add session/cache-based deduplication.
Doctrine flush timing
If the counter mutates a managed entity, the count may be flushed later than expected or combined with other updates. Call the bundle’s persistence method deliberately and test inside transactions.
Concurrency
Read-modify-write counters can lose increments under load. Prefer an atomic SQL increment or a dedicated counter table if accuracy matters. Treat the bundle’s default behavior as “best effort” until proven otherwise.
Configuration drift
Config key names and bundle aliases can change between versions. Copy the exact YAML key from Configuration.php, not from a blog post. Clear cache after config changes:
php bin/console cache:clear
Debugging
Use debug:container to find the real service ID:
php bin/console debug:container view_counter
Use debug:router and Symfony Profiler to confirm which requests are being counted.
Extension points
The safest extension points are usually:
Graceful degradation
If counting fails, do not break the page. Wrap the counter call in a try/catch or move it to an async handler, and log the failure. A view counter should be non-critical infrastructure.
How can I help you explore Laravel packages today?