cyrildewit/eloquent-viewable
Eloquent Viewable is a minimalistic analytics package for Laravel that adds seamless view tracking to Eloquent models, with cooldowns, queued recording, unique counts, and period-based queries.
views table (viewable_type/viewable_id, visitor, collection, viewed_at); aggregation happens at read time through a fluent views($model) API, Eloquent scopes, and a Period value object.composer require, publish migrations, php artisan migrate, add Viewable interface + InteractsWithViews trait, call views($post)->record() in the controller show action.View/Views models, custom visitor resolution, pluggable crawler detection, macros, view collections, per-call or global queueing, built-in caching, and removeViewsOnDelete. That covers most enterprise "but we need tenant scoping / custom attribution" objections without forking.count() on a detail page is fine with the right index; orderByViews on a paginated listing is the scaling cliff, typically appearing as the slowest query on the page once the table reaches tens of millions of rows. Caching helps, but the package does not ship rollup/pre-aggregation — that becomes custom work.ViewRecorded listeners execute in the worker without session, cookies, request(), or auth()->user(). Listeners reading request context fail silently — the failure mode is wrong analytics discovered weeks later.ViewRecorded listeners planned, and can each survive without request context?RefreshDatabase test harness, same event/observability stack. Instrumentation sits in the same controller methods the team already owns.show actions, add composite indexes on (viewable_type, viewable_id, viewed_at) plus visitor/collection columns. Introduce caching only after read p95 breaches its target — not preemptively.views_count while rows start landing, assert that views($m)->count() converges with the legacy counter over a full period, then switch reads and drop the column. This is the one migration path where correctness is cheap to prove.^7 for Laravel 6–12 / PHP 7.4+, ^8 for Laravel 13 / PHP 8.5+. Lock the line to the platform and block bumps until the platform upgrade is scheduled and tested.COUNT and correlated-subquery behavior of the ordering scopes on the actual engine in the spike — index and planner behavior differ meaningfully across MySQL, Postgres, and SQLite.viewable_id does not scope by tenant. Use collections or a custom View model carrying tenant_id, and decide this before writing rows, not after.count() and orderByViews p95 on the target DB and engine.ViewTracker wrapper so a backend swap is a one-file change.orderByViews, discovered only once production data volume arrives.removeViewsOnDelete disabled, orphaned rows accumulate; enabled, bulk deletes on models with millions of views can stall the delete request.How can I help you explore Laravel packages today?