To implement a "sort by recent views" feature without updating the database on every model retrieval, you can use a combination of caching and a separate table to track views. Here's a step-by-step solution:
-
Create a Views Table: Create a new table to store the view counts and timestamps for each model.
php artisan make:migration create_views_table --create=views
In the migration file:
Schema::create('views', function (Blueprint $table) {
$table->id();
$table->morphs('viewable'); // This will allow the table to be used for multiple models
$table->integer('views')->default(0);
$table->timestamps();
});
-
Create a Viewable Trait: Create a trait to handle the view logic.
namespace App\Traits;
use App\Models\View;
trait Viewable
{
public function views()
{
return $this->morphOne(View::class, 'viewable');
}
public function incrementViews()
{
$view = $this->views()->firstOrCreate([]);
$view->increment('views');
$view->touch(); // Update the timestamp
}
}
-
Use the Trait in Your Models: Use the
Viewable trait in the models you want to track views for.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\Viewable;
class Post extends Model
{
use Viewable;
}
-
Increment Views on Retrieval: Increment the view count when the model is retrieved.
$post = Post::find($id);
$post->incrementViews();
-
Sort by Recently Viewed: To sort by recently viewed, you can join the views table and order by the updated_at column.
use App\Models\Post;
use App\Models\View;
$posts = Post::leftJoin('views', function($join) {
$join->on('posts.id', '=', 'views.viewable_id')
->where('views.viewable_type', '=', Post::class);
})
->orderBy('views.updated_at', 'desc')
->get();
This approach ensures that you are not updating the main model's table on every view, but rather a separate views table. This also allows you to sort by the most recently viewed items efficiently.