0
0

コメントした人のアバターを並べて表示するためのコード

Posted at

この記事の内容

デイトラWeb開発コースの課題で以下のようなものがあり、結構手間取ったので実装した方法を残します。

スクリーンショット 2024-09-01 215629.png

使っている技術とバージョン

  • Rails 6.0.6.1
  • ruby 2.7.7
  • scss

モデルの関係性

  • UserモデルにTaskモデルが1対Nの関係で紐づいている
  • Userモデルにavatarの画像が保持されている
  • CommentsモデルはUserモデル、Taskモデルとそれぞれ1対Nの関係で紐づいている
  • UserモデルにはBoardモデルも1対Nの関係で紐づいており、BoardモデルにはTaskモデルが1対Nの関係で紐づいている

得たい成果

  • views/tasks/index.html.haml上に記事の作成者とコメントした人のアバターを重ねて表示したい

ポイント

  • ユーザーをユニーク化して表示する必要がある
    • コメントごとにアバターを表示すると、同じユーザーのAvatarが複数回出てしまうため
  • showではなくindex画面で表示する必要がある
    • showだとtaskが一意に決まるので簡単
    • indexだとtasksの配列から要素を取り出して設定する必要があるため手数が増える

実装したコード

task_controller

  def index
    @tasks = Task.where(board_id: params[:board_id])
    @users_with_avatars = {}
    
    @tasks.each do |task|
      @users_with_avatars[task.id] = avatars(task)
    end
  end

  private
  def avatars(task)
    avatars = []
    avatars << task.user.id

    comments = task.comments.all
    comments.each do |comment|
      if !avatars.include?(comment.user.id)
        avatars << comment.user.id
      end
    end

    avatars
  end

tasks/index.html.haml

.card_avatars
  - @users_with_avatars[task.id].each do |avatar|
    - if User.find(avatar).avatar.attached?
      = image_tag task.user.avatar, class: 'card_avatar'
    - else
      = image_tag 'default-avatar.png', class: 'card_avatar'

詳細

task_controller

  private
  def avatars(task)
  • avatarsメソッドを定義
  • indexアクション内での使用のみを想定しているのでpr-ivateで設定
  • indexアクション内で@taskを引数にするために(task)を記載
    avatars = []
  • 後にビュー側でeachメソッドで吐き出せるように配列として定義
  • ユニーク化するため、user.idの配列を取得して入れていく
    avatars << task.user.id
  • タスクの作成者のアバターは必ず入るためavatarsに追加
  • 定義したときに入れてもいいけど、わかりやすさを重視するため別行に記載
    comments = task.comments.all
    comments.each do |comment|
  • taskに紐づいたcommentをすべて取得
      if !avatars.include?(comment.user.id)
  • コメントした人のユニーク化のための記述
  • これがないと、一人の人が複数回コメントすると複数個の同一人物のアバターが表示される
  • includeメソッド:配列内に指定した要素があればture、なければfalseを返すメソッド
  • avatars.include?(comment.user.id)と記述すると配列avatars内にcomment.user.id(コメントした人のユーザーID)があればture、なければfalseを返す
  • 今回の場合、avatarsの中にすでに同じuser.idがあればfalseにしたいので、!をつけて論理式を逆にする
        avatars << comment.user.id
      end
    end
  • avatars.include?(comment.user.id)がfalseだった場合=avatars内にそのuser.idが含まれない場合、avatarsにuser.idを追加する
    avatars
  end
  • これ忘れがち
  • メソッドは最後の行の記述をreturnとして返すから、これがないと配列avatarsを取得できない
  def index
    @tasks = Task.where(board_id: params[:board_id])
  • もともと記載していた内容
  • ビュー側で@tasks.each doを使って全タスクを表示するため記載していた
    @users_with_avatars = {}
  • ハッシュを作る
  • このあとに@tasksからeach doで取得したtaskを使って、keyにtaskのid、valueに先ほど定義したavatarsメソッドで作る配列とするため
  • このハッシュがあると、ビュー側の@tasks.each do |task|でkeyを指定して、特定のタスクのavatars(コメントしたユニーク化されたユーザーの配列)を取得できる
    @tasks.each do |task|
      @users_with_avatars[task.id] = avatars(task)
    end
  end
  • ハッシュ@users_with_avatarsにtaskのidをkeyとしてavatars(コメントしたユニーク化されたユーザーの配列)を入れていく

tasks/index.html.haml

.card_avatars
  • CSS適用のためのdiv
  • CSSはシンプルなので今回は記載しません
  - @users_with_avatars[task.id].each do |avatar|
  • 裏側で作成した@users_with_avatarsにキーを渡して、配列avatarsを取得
  • user.idが詰まった配列avatarsなので、user.idを一つ一つ取り出し以下にavatarとして活用
    - if User.find(avatar).avatar.attached?
  • Userモデルから、idをもとにインスタンスを取得
  • 取得したインスタンスにavatarがあるかどうかを判定
    • アバターの設定は必須としていないため
      = image_tag task.user.avatar, class: 'card_avatar'
    - else
      = image_tag 'default-avatar.png', class: 'card_avatar'
  • アバターが設定されているユーザーならアバターを表示
  • そうでないならデフォルトのアバターを表示
  • これをavatarの要素分繰り返す
  • 表示されたアバターをCSSで横並びにしてネガティブマージンを使って重ねる

学びになった点

  • includeメソッドと!でユニーク化ができる
  • @tasksのような配列を使わないといけない状況下の場合、ハッシュを使ってidをキーにして設定するとうまくいく
0
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
0
0