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 third-party analytics. Record and count total or unique views, filter by date periods, order models by views, apply cooldowns, and optionally ignore crawlers. Stores each view as a DB record for flexible querying.

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(); // Auto-detects crawlers
        return view('post.show', compact('post'));
    }
    

Implementation Patterns

Core Workflows

  1. View Tracking:

    • Default: views($model)->record() (ignores crawlers/DNT headers).
    • With Cooldown:
      views($post)->cooldown(now()->addHours(2))->record();
      
    • Custom Collections:
      views($post)->collection('admin_dashboard')->record();
      
  2. Querying Views:

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

    // Global scope (descending)
    Post::orderByViews()->get();
    
    // Periodic scope
    Post::orderByViews('asc', Period::pastMonths(1))->get();
    
  4. Type-Level Analytics:

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

Integration Tips

  • Middleware: Use views($model)->record() in middleware to track API/SPA routes.
  • Events: Trigger view tracking via ModelObservers for consistency:
    public function retrieving(Model $model) {
        if ($model instanceof Viewable) {
            views($model)->record();
        }
    }
    
  • Caching: Cache frequent queries with remember():
    views($post)->period(Period::pastWeek())->remember(3600)->count();
    

Gotchas and Tips

Pitfalls

  1. Crawler Detection:

    • Postman/Scrapers are blocked by default. Use views($post)->ignoreCrawlers(false)->record() to bypass.
    • Test locally with config(['eloquent-viewable.ignore_crawlers' => false]).
  2. Database Bloat:

    • Each view creates a row. For high-traffic sites:
      • Add indexes: viewable_id, viewable_type, and visitor (if unique views matter).
      • Archive old views via scheduled jobs:
        View::where('created_at', '<=', now()->subYear())->delete();
        
  3. Cooldown Quirks:

    • Session-based cooldowns expire when the session ends. Use cooldown(now()->addDays(7)) for persistent delays.
  4. Caching Caveats:

    • remember() only caches count() results. For orderByViews, cache counts in model columns (e.g., views_count) and update via scheduled jobs.

Debugging

  • Query Logs: Enable Laravel’s query logging to inspect generated SQL:
    \DB::enableQueryLog();
    views($post)->period(Period::pastDay())->count();
    \DB::getQueryLog();
    
  • Visitor IP: Override visitor detection by binding a custom Visitor class:
    $this->app->bind('eloquent-viewable.visitor', function () {
        return new CustomVisitor(request()->ip());
    });
    

Extension Points

  1. Custom View Model: Extend the View model to add metadata (e.g., user_agent):

    class CustomView extends \CyrildeWit\EloquentViewable\View {
        protected $fillable = ['user_agent'];
    }
    

    Bind it in AppServiceProvider:

    $this->app->bind('eloquent-viewable.view', CustomView::class);
    
  2. Macros: Add reusable methods to the Views facade:

    \CyrildeWit\EloquentViewable\Facades\Views::macro('trending', function () {
        return $this->period(Period::pastDays(7))->orderBy('created_at', 'desc')->limit(10);
    });
    

    Usage: views($post)->trending()->get().

  3. Crawler Detection: Replace the default detector:

    $this->app->bind('eloquent-viewable.crawler-detector', CustomCrawlerDetector::class);
    

Performance

  • Batch Updates: For bulk view updates, use View::insert() instead of individual record() calls.
  • Indexing: Add a composite index for viewable_type + viewable_id + created_at if querying by time ranges frequently.

```markdown
### Pro Tips
- **Soft Deletes**: If your models use soft deletes, ensure `View` model also implements `SoftDeletes` to avoid orphaned records.
- **API Tokens**: For API-driven views, use `Visitor::fromToken($request->bearerToken())` to track authenticated users uniquely.
- **Testing**: Mock the `Visitor` class in tests to avoid real database writes:
  ```php
  $this->app->instance('eloquent-viewable.visitor', MockVisitor::class);
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport