Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Eloquent Viewable Laravel Package

cyrildewit/eloquent-viewable

Track page views on Eloquent models without external analytics. Record views (with optional cooldown), count totals or unique views, filter by period, order models by popularity, and ignore bots/crawlers. Stores each view as a DB record for flexible reporting.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require cyrildewit/eloquent-viewable
    php artisan vendor:publish --provider="CyrildeWit\EloquentViewable\EloquentViewableServiceProvider" --tag="migrations"
    php artisan migrate
    

    (Optional: Publish config with --tag="config")

  2. Model Integration: Add the Viewable interface and InteractsWithViews trait to your Eloquent model:

    use CyrildeWit\EloquentViewable\Contracts\Viewable;
    use CyrildeWit\EloquentViewable\InteractsWithViews;
    
    class Post extends Model implements Viewable {
        use InteractsWithViews;
    }
    
  3. First Use Case: Track views in a controller:

    public function show(Post $post) {
        views($post)->record(); // Log view
        return view('post.show', compact('post'));
    }
    

Implementation Patterns

Core Workflows

  1. View Tracking:

    • Standard: views($model)->record();
    • With Cooldown: views($model)->cooldown(30)->record(); (30 minutes)
    • Custom Collections: views($model)->collection('premium')->record();
  2. Querying Views:

    • Total Count: views($post)->count();
    • Periodic Count: views($post)->period(Period::pastDays(7))->count();
    • Unique Views: views($post)->unique()->count();
  3. Model Ordering:

    // Order posts by views (descending)
    Post::orderByViews()->get();
    
    // Order by unique views (ascending)
    Post::orderByUniqueViews('asc')->get();
    
    // Periodic ordering
    Post::orderByViews('asc', Period::pastMonths(1))->get();
    
  4. Type-Level Analytics:

    // Total views for all Posts
    views(new Post())->count();
    
    // Or via class string
    views(Post::class)->count();
    

Integration Tips

  • Middleware: Create middleware to auto-record views:
    public function handle($request, Closure $next) {
        views($request->post)->record();
        return $next($request);
    }
    
  • API Endpoints: Expose view counts via API:
    Route::get('/posts/{post}/views', function (Post $post) {
        return response()->json(['count' => views($post)->count()]);
    });
    
  • Caching Strategy: Cache frequent queries:
    // Cache for 1 hour
    views($post)->remember(3600)->count();
    

Gotchas and Tips

Pitfalls

  1. Crawler Filtering:

    • Default behavior blocks crawlers (e.g., Postman). Use views($model)->ignoreCrawlers(false)->record(); to bypass.
  2. Database Bloat:

    • Each view creates a row. For high-traffic sites:
      • Add indexes: viewable_id, viewable_type, and optionally visitor.
      • Archive old views via scheduled jobs.
  3. Session Cooldown:

    • Cooldowns rely on sessions. Ensure SESSION_DRIVER is configured (e.g., file, database).
  4. Unique Views Accuracy:

    • Unique counts use visitor cookies/IPs. Inconsistent cookies (e.g., private browsing) may skew results.

Debugging

  • Query Logging: Enable Laravel’s query log to inspect generated SQL:
    DB::enableQueryLog();
    views($post)->count();
    dd(DB::getQueryLog());
    
  • View Count Mismatches: Verify visitor column values in the views table match expected identifiers.

Extension Points

  1. Custom Visitor Data: Extend the Visitor model to store additional metadata:

    class CustomVisitor extends \CyrildeWit\EloquentViewable\Visitor {
        protected $casts = ['user_agent' => 'string'];
    }
    

    Bind it in config/eloquent-viewable.php:

    'visitor_model' => \App\Models\CustomVisitor::class,
    
  2. Override Core Classes: Replace the default Views facade with a custom implementation:

    // config/app.php
    'aliases' => [
        'views' => App\Services\CustomViews::class,
    ];
    
  3. Macros: Add custom methods to the Views class:

    use CyrildeWit\EloquentViewable\Views;
    
    Views::macro('recent', function () {
        return $this->period(Period::pastDays(1));
    });
    
    // Usage: views($post)->recent()->count();
    
  4. Crawler Detection: Replace the default crawler detector:

    'crawler_detector' => \App\Services\CustomCrawlerDetector::class,
    

Configuration Quirks

  • Default Collection: Set a default collection in config/eloquent-viewable.php:
    'default_collection' => 'default',
    
  • Ignore DNT Header: Disable "Do Not Track" filtering:
    'ignore_dnt_header' => false,
    
  • Remove Views on Delete: Enable per-model:
    protected $removeViewsOnDelete = true;
    

Performance Tips

  • Batch Updates: For bulk view updates, use Laravel’s update():
    Post::query()->update([
        'views_count' => views($post)->count(),
    ]);
    
  • Scheduled Jobs: Periodically update cached counts:
    // app/Console/Commands/UpdateViewCounts.php
    public function handle() {
        Post::chunk(100, function ($posts) {
            foreach ($posts as $post) {
                $post->update(['views_count' => views($post)->count()]);
            }
        });
    }
    
  • Indexing: Add composite indexes for complex queries:
    Schema::table('views', function (Blueprint $table) {
        $table->index(['viewable_type', 'viewable_id', 'visitor']);
    });
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope