1
0

未ログインユーザーの閲覧数カウント

Posted at

はじめに

絶賛ポートフォリオ制作中です!
今回は未ログインのユーザーが投稿詳細画面を表示した時に、
閲覧数をカウントするように実装したためアウトプットします✍️

※旅行やデートのプランを共有するアプリなので、投稿機能はPostではなくPlanを使用しています!

ER図

モデル、マイグレーションファイル作成

モデル作成

$ rails g model ViewCount user:references plan:references

referencesとは(直訳:参照、参考)
名前の通り、作成済みのテーブルを参照する場合に使用します
コマンドを実行すると下記カラムがマイグレーションファイルに自動追加されます

t.references :user, null: false, foreign_key: true
t.references :plan, null: false, foreign_key: true

ここで指定しなくとも、後からマイグレーションファイルに追加可能です!
のちにモデルに記載する「belongs_to」を自動で記載もしてくれます
※「has_many」は追加してくれないので注意⚠️

以下の記事を参照しました!

マイグレーションファイル

足りないところを追加したり、自動入力されている箇所を修正します

xxxxx_create_view_counts.rb
class CreateViewCounts < ActiveRecord::Migration[6.1]
  def change
    create_table :view_counts do |t|
      # 初期の記述と変わっているため注意!↓
      t.references :user, null: true, foreign_key: true
      t.references :plan, null: false, foreign_key: true
      t.string :user_or_ip, null: false

      t.timestamps
    end
    # ユーザーと投稿の組み合わせを組合せを一意に保つ為の記述
    add_index :view_counts, [:user_or_ip, :plan_id], unique: true
  end
end

注意⚠️
userのnull: falsenull: trueに変更します
通常はnullを許可しませんが、今回は未ログインのユーザーの閲覧数をカウントする必要が(current_userがnilになる可能性が)あるためです!

t.string :user_or_ip, null: falseadd_index :view_counts, [:user_or_ip, :plan_id], unique: trueを追加しましょう!

忘れないうちにマグレートします!

$ rails db:migrate

アソシエーション

user.rb
class User < ApplicationRecord
:
has_many :view_counts, dependent: :destroy
:
end
plan.rb
class Plan < ApplicationRecord
:
has_many :view_counts, dependent: :destroy
:
end
view_count.rb
class ViewCount < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :plan

  validates :user, uniqueness: { scope: :plan_id }, if: -> { user.present? }
  validates :user_or_ip, uniqueness: { scope: :plan_id }, if: -> { user.nil? }
end

optional: trueとは 🌱
belongs_toの外部キーのnilを許可するという意味です
今回の例で言うと、未ログインユーザーの閲覧数をカウントする場合、user_idがnilになります!このオプションを指定しないとuser_idのnillを許可していないので、エラーが発生してしまいます
このオプションはデフォルトではfalseになっています

こちらの記事がわかりやすかったです!

バリデーション

ユーザーと投稿の組み合わせが一意であることを確認しています

if: -> { user.present? }
userがnilでない場合はuserとplan_idの組み合わせを確認

if: -> { user.nil? }
userがnilの場合は、user_or_ipとplan_idの組み合わせを確認

コントローラーに記述

plans_controller.rb
class Public::PlansController < ApplicationController
  def show
    @plan = Plan.find(params[:id])
    user_or_ip = current_user ? current_user.id.to_s : request.remote_ip
    unless ViewCount.find_by(user_or_ip: user_or_ip, plan: @plan)
      ViewCount.create(user_or_ip: user_or_ip, plan: @plan, user: current_user)
    end
  end
end

user_or_ip = current_user ? current_user.id.to_s : request.remote_ip
三項演算子の構文を使用しています

条件 ? 真の場合の値 : 偽の場合の値

current_user ?では現在ユーザーがログインしているかどうかを判定しています

真の場合(ユーザーがログインしている場合)
current_user.id.to_scurrent_userのidを文字列化し、user_or_ipに代入します
⚠️次に記載するipアドレスが文字列なので、合わせるためにidを文字列に変換しています!

偽の場合(ユーザーがログインしていない場合)
request.remote_ip
リクエスト元のユーザーのIPアドレスを取得し、user_or_ipに代入します
初めて知った記述だったため、念の為確認しました↓

plans_controller.rb
    unless ViewCount.find_by(user_or_ip: user_or_ip, plan: @plan)
      ViewCount.create(user_or_ip: user_or_ip, plan: @plan, user: current_user)

find_byでuser_or_ipplanの組み合わせが既にないか確認し、
なければViewCoutデータを作成します(同じクライアントが複数回閲覧しても閲覧数は1)

viewを記述

さいごに、表示したい場所に以下を記述します

plans/show.html.erb
<%= @plan.view_counts.count %>Views

検討するべきポイント

未ログインのユーザーに対してIPアドレスを取得していますが、
同じネットワークを共有する複数のユーザーが同じIPアドレスを使用する場合があります
そのため異なるユーザーの閲覧を区別することができない場合があります
もっといい方法がありましたら教えていただけますと幸いです(これが限界でした…!)

さいごに

閲覧数の記事は沢山あったのですが、未ログインユーザーの閲覧数をカウントする方法が見つからなかったためアウトプットも兼ねて書きました✍️

誤り等ございましたら教えていただけますと幸いです!

参照記事

参考にさせていただきました!ありがとうございます!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0