ilhan_ersoy Bir tane post_has_views isminde bir pivot tablo oluşturun:
Schema::create('post_has_views', function (Blueprint $table) {
$table->foreignId('user_id')->constrained('users');
$table->foreignId('post_id')->constrained('posts');
$table->ipAddress('ip')->nullable();
$table->string('user_agent')->nullable();
$table->timestamp('viewed_at');
$table->unique(['user_id', 'post_id']);
});
app/Models/Pivots/PostView.php:
use Illuminate\Database\Eloquent\Relations\Pivot;
class PostView extends Pivot
{
protected $table = 'post_has_views';
protected $dates = [
'viewed_at',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}
app/Models/Post.php
public function views(): BelongsToMany
{
return $this->belongsToMany(User::class, 'post_has_views')
->using(PostView::class)
->withPivot([
'ip',
'user_agent',
'viewed_at'
]);
}
public function trackView($user, ?string $ip = null, ?string $userAgent = null, ?Carbon $date = null): bool
{
if($user instanceof User) {
$user = $user->id;
}
if(!is_int($user)) {
return false;
}
$this->views()->sync([
$user => [
'ip' => $ip,
'user_agent' => $userAgent,
'viewed_at' => $date ?? now()
]
]);
return true;
}
public function trackViewFromRequest(?Request $request = null, ?Carbon $date = null): bool
{
if(!$request instanceof Request) {
$request = request();
}
return $this->trackView($request->user(), $request->ip(), $request->header('user-agent'), $date);
}
Şimdi artık mesela şöyle yapabilirsiniz:
public function show($slug)
{
$post = Post::where('slug', $slug)
->withCount('views')
->firstOrFail();
$post->trackViewFromRequest();
return view('posts.show', compact('post'));
}
+---------+---------+--------------+----------------------------------------------+---------------------+
| user_id | post_id | ip | user_agent | viewed_at |
+---------+---------+--------------+----------------------------------------------+---------------------+
| 1 | 1 | 88.224.31.55 | Mozilla/5.0 (Windows NT 10.0; Win64; x64)... | 2020-10-13 16:43:23 |
| 1 | 2 | 88.224.31.55 | Mozilla/5.0 (Windows NT 10.0; Win64; x64)... | 2020-10-16 01:26:03 |
| 2 | 5 | 66.126.77.41 | Mozilla/5.0 (Windows NT 10.0; Win64; x64)... | 2020-10-16 02:32:15 |
+---------+---------+--------------+----------------------------------------------+---------------------+
Burada withCount('views')
ile views_count özelliğini de Post modeline eklemiş olduk. Böylece $post->views_count
şeklinde bir yazının kaç kere görüntülendiğini alabiliriz.
Yazıları en çok izlenenler en üstte olacak şekilde sıralamak istersek:
$posts = Post::withCount('views')
->orderByDesc('views_count')
->paginate();
Bu şekilde bir sürü işlem yapılabilir. Hızlıca hazırladığım için bazı şeyler eksik ve gözden kaçırmış olabilirim. Sadece yol göstermesi açısından yazdım.
- User modeline de ilişki eklenerek kullanıcı tarafından bilgiler alınabilir. Kullanıcının en çok baktığı yazılar mesela.
- PostView modeli Pivot yerine düz modele dönüştürülüp ondan yola çıkılabilir ya da DB::table('post_has_views') ile işlemler yapılabilir. Post ve User modelinden yola çıkılarak post_has_views tablosuyla join yapılarak, GROUP BY, HAVING vs... kullanılarak işlemler yapılabilir.
- Bu yaptığım sadece tekil ziyaretleri kaydediyor, bir kullanıcı bir yazıya birden fazla kez baksın istiyorsanız tablodaki unique composite indeksi kaldırmanız lazım ve sync yerine attach kullanmanız lazım. Sorgular vs de ona göre değişecek. Ayrıca sadece giriş yapmış kullanıcıla için.
- ip, user_agent ve viewed_at gibi alanlar sayesinde bir kullanıcı bir yazıyı hangi tarihte hangi ip'den hangi tarayıcı bilgisi ile görmüş bu bilgiye de ulaşmış oluyorsunuz.
Bana göre bu yöntem bir alanın değerini +1 arttırmaktan daha iyi bir yol.