Show popular content from a specific period in Laravel

Table of contents:

# Introduction

When you visit online stores or blogs, you might have noticed that they often have sections like "Users also viewed" or "Popular products". For example, when you are in a product page, you might see a section like this:

It can be useful to show content such as the most visited for the last 24 hours on your website. This could help you to present more relevant resources to your users and increase user engagement.

In this article, we'll learn how to track views and get popular content (ordered by views) from a specific period in Laravel.

# Track views and fetch popular content from a specific period

# Install the cyrildewit/eloquent-viewable package

To handle views, you need to install the cyrildewit/eloquent-viewable package:

composer require cyrildewit/eloquent-viewable

Publish the migrations and run the migrate command:

php artisan vendor:publish --provider="CyrildeWit\EloquentViewable\EloquentViewableServiceProvider" --tag="migrations"
php artisan migrate

Then, add the InteractsWithViews trait to your model that you want to track views. The model should also implement the Viewable interface:

use CyrildeWit\EloquentViewable\Contracts\Viewable;
use CyrildeWit\EloquentViewable\InteractsWithViews;

class Course extends Model implements Viewable
{
    use InteractsWithViews;
}

# Record views

To make a view record after visiting our resource, you can dispatch a job after the response is sent to the browser:

dispatch(function () {
    views($course)->record();
})->afterResponse();

# Show popular content from the last 24 hours

To show resources that have been viewed the most in the last 24 hours, you can use the orderByViews method (it's a scope from InteractsWithViews trait) that is used to order your models by the amount of views they have in a given period:

use App\Models\Course;
use CyrildeWit\EloquentViewable\Support\Period;

$courses = Course::orderByViews('desc', Period::subHours(24))
            ->take(20)
            ->get();

# Remove views older than 24 hours

If we only need to keep views in our database from the last day, we can extend the default View model and use the Prunable trait to delete views older than 24 hours:

<?php

declare(strict_types=1);

namespace App\Models;

use CyrildeWit\EloquentViewable\View as ViewModel;
use Illuminate\Database\Eloquent\Prunable;

class View extends ViewModel
{
    use Prunable;

    /**
     * Get the prunable model query.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function prunable()
    {
        return static::where('viewed_at', '<=', now()->subHours(24));
    }
}

In App\Http\Kernel.php, schedule the model:prune command to delete views older than 24 hours every day:

use App\Models\View;

$schedule->command('model:prune', [
    '--model' => [View::class],
])->daily();

# Other periods

You can also use the Period class to fetch popular content from other periods. For example, to fetch popular content from the last 7 days:

use CyrildeWit\EloquentViewable\Support\Period;

$courses = Course::orderByViews('desc', Period::subDays(7))
            ->take(20)
            ->get();

More periods are available in the Period class, such as subMinutes, subHours, subDays, subWeeks, subMonths, and subYears. Check all in the official documentation.