0
0

【Rails】json形式のレスポンスを整形して、画像ファイルのURLをフロントに渡す方法

Posted at

はじめに

Ajaxでコメント一覧の表示・コメント投稿の機能を実装する際に、コメントした人のアバターも入れたかったのですが、やり方が分からなくて苦戦したので実装方法を残しておきます。

やりたいこと

javascriptからaxios.getでリクエストを送った際に下記の情報がほしい

  • commentの内容
  • commentした人のusername
  • commentした人のavatar

状況

  • 関連するモデル
    • Userモデル
    • Commentモデル
    • Proflieモデル
  • 各モデルの関連性
    • User has_many commnets
    • User has_one profile
    • Post has_many commnets
  • 各モデルに紐づく要素(今回の件に関係するところのみ)
    • profilesテーブルにavatar画像がある
    • commentsテーブルにcontent(コメントの内容)がある
    • usersテーブルにusernameがある

やり方

方向性

  1. ruby側でcomment.user.profile.avatar のPathを動的に生成
  2. 上記のpathも含めたjson形式のレスポンスを作成し、javascript側へ渡す
  3. json形式のレスポンスを展開し、htmlとして出力。その際にimgタグを生成し、src属性に画像のpathを動的に記述する

comment.user.profile.avatar のPath生成方法

Profileモデルに画像のPathを生成するためのメソッドを定義する

# app/models/profile.rb
class Profile < ApplicationRecord
  has_one_attached :avatar

  def avatar_path
    Rails.application.routes.url_helpers.rails_blob_path(avatar) if avatar.attached?
  end
end

ポイント

Rails.application.routes.url_helpers

rails_blob_urlメソッドを使ってURLを生成したいのですが、Controllerやview以外だと上記のようにアクセスする必要があるようです。

rails_blob_path(avatar)

rails_blob_path()はActiveStorageで使えるメソッドです。
()内の画像の相対パスを返してくれます。
ちなみに、絶対Path(URL)と相対Pathどちらがいいかでも悩んだのですが、今回は外部から直接画像にアクセスする必要がなく、アプリケーション内部での参照をする想定なので相対Pathを選択しました。

avatar_path(avatar)のように引数を設定しなくてもいい理由

微妙に悩んだのですが、引数の設定は不要でした。
理由は、ruby側でcurrent_user.profile.avatarでavatarを参照できるように、json形式のレスポンスをシリアライザーで作成するときもavatarの参照が可能なためです。

この辺の関数やメソッドの引数の考え方についてはまだまだ理解不足なので精進します。。。

json形式のレスポンスの整形方法

前提としてActiveSerializerを導入しているものとします。
まず関連するモデルのSerializerを作成します

rails generate serializer comment
rails generate serializer user
rails generate serializer profile

次に各serializerファイルに下記のように記述します。

# app/serializers/comment_serializer.rb
class CommentSerializer < ActiveModel::Serializer
  attributes :id, :content
  belongs_to :user
end

# app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name
  has_one :profile
end

# app/serializers/profile_serializer.rb
class ProfileSerializer < ActiveModel::Serializer
  attributes :avatar_path
end

Controller側でjson形式でレンダリングするように設定します。

# app/controllers/comments_controller.rb
  def index
    post = Post.find(params[:post_id])
    comments = post.comments
    render json: comments, include: ['user.profile']
  end

ポイント

serializerファイルの記述方法

attributesにカラム名を指定することで、そのカラムの情報を出力できます。
また、メソッドを指定することもでき、その場合メソッドの返り値を出力できます。

Modelファイルと同じように、モデル間の関係性を記述することで、出力の形を整形できます。
今回のような記述内容だと、下記のように出力されます。

{
  "id": 1,
  "content": "hogehoge",
  "user": {
    "id": 1,
    "name": "hoge hoge",
    "profile": {
      "avatar_path": "/rails/active_storage/blobs/12345/user_avatar.png"
    }
  }
}

わりとシンプル!

render json: comments, include: ['user.profile']について

本来は、include以下はいらないはずなのですが、
今回はinclude以下を記述しないとproflieをアウトプットに含めてくれませんでした。

この辺の仕様がどうなっているかはわからなかったのですが、今回のように明示する必要があるケースもありそうです。

JavaScript側でhtmlを生成する

$(document).on('turbolinks:load', () => {
  const postId = $('#commentTitle').data('post-id')

  axios.get(`/posts/${postId}/comments`)
  .then(response => {
    const comments = response.data
    comments.forEach(comment => {
      const commentHtml = `
        <div class="comment">
          <img src="${comment.user.profile.avatar_path}" alt="User Avatar">
          <div class="commentContent">${comment.content}</div>
        </div>
      `
      $('.comments-container').append(commentHtml)
    })
  })

こんな感じでimgのsrc属性に取得したPathを指定することでうまく表示されました!

おわりに

json形式でレスポンスを返したいことは多くなりそうなので、整形の仕方は覚えておこうともいますー!

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