Eloquent Viewable Documentation

repository·main·Indexed 21 days ago

https://github.com/cyrildewit/eloquent-viewable

A minimalistic analytics package for Laravel that provides view tracking for Eloquent models by storing views as individual database records. It supports calculating total and unique views, filtering by custom time periods using the Period class, queueing view records for high-traffic pages, and ordering models by view count via query scopes.

Tokens
7.7K
Snippets
34
Records
45
Agent score
75%

What's inside Eloquent Viewable

  1. Overview of Eloquent Viewable

    main
    Eloquent Viewable is a minimalistic analytics package for Laravel designed to track views for Eloquent models. Unlike simple counter increments, it stores each view as an individual database record. This allows for advanced analytics such as calculating total views, unique visitors, and filtering views by custom time periods directly within your application.
  2. How the new Cache Key format works

    main

    The internal CacheKey class now uses a readable prefix followed by a hash of the count's full identity ({prefix}:{morph class}:{key}:{digest}). This ensures shorter, fixed-length keys that avoid backend limits (like Memcached's 250-byte cap) and prevent collisions across different models.

    No action is required for standard usage. Existing cache entries under the old format will be ignored and recomputed on the next count() call. If you want to clear old entries immediately, run php artisan cache:clear.

  3. How the `Period` class works in v8.0.0

    main

    In v8.0.0, Period is now a final, immutable (readonly) value object.

    Key changes:

    • Immutability: Setters like setStartDateTime() have been removed. You must build a new period instead of mutating an existing one.
    • No Subclassing: The class is now final and cannot be extended.
    • Removed Methods: The following are no longer available:
      • getStartDateTimeString(), getEndDateTimeString(), getStartDateTimestamp(), getEndDateTimestamp() (Use DateTimeInterface getters instead).
      • PAST_* and SUB_* constants.
      • sub(), subToday(), subNow() static helpers.
      • getSubType(), getSubValue(), hasFixedDateTimes().

    Usage Pattern: To get formatted strings or timestamps, use the DateTimeInterface returned by getStartDateTime() or getEndDateTime().

    -$period->getStartDateTimeString();
    +$period->getStartDateTime()?->format('Y-m-d H:i:s');
    
    -$period->getStartDateTimestamp();
    +$period->getStartDateTime()?->timestamp;
  4. Set up Eloquent Viewable database migrations

    main

    After installing the package, you must publish and run the migrations to create the necessary tables for storing view records.

    1. Publish the migrations:
    php artisan vendor:publish --provider="CyrildeWit\EloquentViewable\EloquentViewableServiceProvider" --tag="migrations"
    1. Run the migrations:
    php artisan migrate
    php artisan vendor:publish --provider="CyrildeWit\EloquentViewable\EloquentViewableServiceProvider" --tag="migrations"
    php artisan migrate
  5. Queue view recording

    main

    On high-traffic pages, you can defer the database write to a queued job using the queue() method. This keeps the request fast.

    Individual Queueing

    Use views($post)->queue()->record(); to queue a specific view.

    Global Queueing

    You can enable queueing globally in the eloquent-viewable.php config file:

    'queue' => [
        'enabled' => true,      // queue every recorded view
        'connection' => null,   // null uses the default queue connection
        'queue' => null,        // null uses the connection's default queue
    ],

    If global queueing is enabled, you can force a synchronous record using views($post)->queue(false)->record();.

    Important: Request Context

    When a view is queued, the ViewRecorded event is dispatched from the queue worker. Listeners will run without request context (session, cookies, request(), and auth()->user() will be unavailable). If your listener needs request-derived data, capture it during the request and pass it to the listener manually.

    // Queue an individual view on the fly
    views($post)->queue()->record();
  6. Prepare your Eloquent model for views

    main

    To associate views with a model, the model must implement the CyrildeWit\EloquentViewable\Contracts\Viewable interface and use the CyrildeWit\EloquentViewable\InteractsWithViews trait.

    use Illuminate\Database\Eloquent\Model;
    use CyrildeWit\EloquentViewable\InteractsWithViews;
    use CyrildeWit\EloquentViewable\Contracts\Viewable;
    
    class Post extends Model implements Viewable
    {
        use InteractsWithViews;
    
        // ...
    }
  7. Migrate the `visitor` column type to string

    main

    In v8.0.0, the create_views_table migration stub defines the visitor column as a string (VARCHAR(255)) instead of text to allow direct indexing.

    If you have already run the migration and wish to adopt the new type on an existing table, you must create a new migration. Note that on large tables this will rewrite the table, and any visitor value longer than 255 characters will be truncated (though the built-in identifier is only 80 characters).

    Schema::table('views', function (Blueprint $table) {
        $table->string('visitor')->nullable()->change();
    });
  8. Upgrade requirements for v8.0.0

    main

    When upgrading to v8.0.0, ensure your environment meets the following minimum requirements:

    • PHP: ^8.5 (previously supported ^7.4 || ^8.0)
    • Laravel: 13 only (support for Laravel 6 through 12 has been dropped)
    • Carbon: ^3.0 only (support for Carbon 2 has been dropped)
  9. Publish Eloquent Viewable configuration

    main

    If you need to customize the package behavior, you can publish the configuration file using the following command:

    php artisan vendor:publish --provider="CyrildeWit\EloquentViewable\EloquentViewableServiceProvider" --tag="config"
  10. How the Views API works

    main

    The Views class acts as a fluent builder for managing view interactions with Eloquent models.

    1. Targeting: You start by calling forViewable($model) to tell the service which model is being viewed.
    2. Configuration: You chain methods like unique(), period(), or cooldown() to define the constraints of your query or recording action.
    3. Execution: You call an execution method, typically count() to retrieve data or record() to persist a new view.

    This pattern allows you to use the same service for both high-frequency recording (often queued) and complex analytical querying (often cached).

  11. Configure connection and cache store defaults

    main

    The default configuration for models.view.connection and cache.store has changed to null. A null value defers to the application's default database connection and default cache store.

    Important considerations:

    • Laravel 11+ Compatibility: Laravel 11 renamed CACHE_DRIVER to CACHE_STORE. If your published config still uses env('CACHE_DRIVER', 'file'), it may ignore your application's actual cache configuration. Set cache.store to null or env('CACHE_STORE') to fix this.
    • Fallback Behavior: If you previously relied on the file driver fallback, you should now set cache.store explicitly instead of using null.