Tracking Read/Unread or Viewed/NotViewed

This is a how-to, not a help-request:

Tracking Views of a record, per user.

views migration:
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('views', function (Blueprint $table) {
            $table->id();
            $table->foreignUlid('user_id')->nullable()->constrained()->cascadeOnDelete()->cascadeOnUpdate();
            $table->foreignUlid('post_id')->nullable()->constrained()->cascadeOnDelete()->cascadeOnUpdate();
            $table->timestamps();
        });
    }


View model:
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class View extends Model
{
    use HasFactory;

    protected $fillable = [
        'post_id',
        'user_id',
    ];

    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class, 'post_id');
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }
}


User model:
    public function views(): HasMany
    {
        return $this->hasMany(View::class, 'user_id', 'id');
    }


Post
model:
    public function trackView(?User $user): void
    {
        $user_id = $user?->id ?? Auth::user()->id;
        View::create([
            'user_id' => $user_id,
            'post_id' => $this->id,
        ]);
    }

    public function getIsViewedAttribute(?User $user)
    {
        $user_id = $user?->id ?? Auth::user()->id;
        return $this->views->where('user_id', $user_id)->count();
    }


Then, for the component that "shows" the record, set its mountUsing() to track the "view":
 ->mountUsing(function (Post $record) {
    $record->trackView(Auth::user());
})

CSS can be used to set a class when the isViewed() attribute is truthy.
Was this page helpful?