LoginSignup
2
4

More than 3 years have passed since last update.

【Rails】掲示板アプリでTopicとPostを関連づける【アソシエーション】

Last updated at Posted at 2020-02-24

はじめに

現在、ポートフォリオとして掲示板アプリを作成しており、題目にある「TopicとPostを紐づける」部分で時間かかったので備忘録として。

早速ですが、紐づけるために必要な要素として以下の2点を中心に記載します。

  1. model側の修正
  2. controller側の修正

やりたいこと

複数あるPostは必ず一つのTopicに紐づく
PostやTopicは必ず一つのUserに紐づく
各modelを関連付けて、「@post.topic.title」のような形で値を取得したい

環境

macOS:10.14.6
Ruby:2.5.7
Rails:5.2.4.1

model側の修正(アソシエーション)

今回登場するmodelは、以下の3つ。
・Topic
・User(deviseを使ってます)
・Post

Topicがスレッド、Postがスレッドに対するレスで、
Topic(1)---(多)Post
User(1)---(多)Post/Topic
というような位置付けになります。

今回の場合は既に各modelを作成(generate)済みだったので、アソシエーション用のマイグレーションファイルを作って、マイグレートしていく形で進めます。

マイグレーションファイルを作成する

$ rails g migration AddUserToTopic
$ rails g migration AddTopicToPost

マイグレーションファイルを編集する

文法は、「add_reference table名、reference名」が基本形になります。
indexはオプションなのでお好みで。
ちなみに超ざっくりですが、indexは読み込み/取得速度を上げてくれるものです
(但し、書き込み速度は遅くなるので注意が必要)

20200222110925_add_user_to_topic.rb
class AddUserToTopic < ActiveRecord::Migration[5.2]
  def change
    add_reference :topics, :user, index: true #追記箇所
  end
end
20200223032448_add_topic_to_post.rb
class AddTopicToPost < ActiveRecord::Migration[5.2]
  def change
    add_reference :posts,:topic #追記箇所
  end
end

マイグレートする

$ rails db:migrate

上記コマンド実行後、以下のようなmigratedが出力されればOK

== 20200223032448 AddTopicToPost: migrating ===================================
-- add_reference(:posts, :topic)
   -> 0.0796s
== 20200223032448 AddTopicToPost: migrated (0.0797s) ==========================

モデルを編集する

topic.rb
class Topic < ApplicationRecord
  validates :title, presence: true
  has_many :posts #追記
  belongs_to :user #追記
end
post.rb
class Post < ApplicationRecord
  belongs_to :topic #追記
  belongs_to :user #追記
end
user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name,:email,:encrypted_password, presence: true
  has_many :topics, through: :posts #追記
end

これでmodel側はOKです!

controller側の修正

各modelの関連付けできたので以下のような形で、関連先の値を拾ってこれるようになりました。細かく言うとパラメーターの中から明示的に値を取得するイメージです。
params[:topic_id]という風な感じで。

posts_controller.rb
class PostsController < ApplicationController

  def new
    @topic_id = params[:topic_id]
    @post = Post.new
  end

  def create
    @post = Post.new
    @topic_id = params[:topic_id] #paramsからtopic_idを取得し、インスタンス変数@topic_idに代入
    @post.topic_id = @topic_id #上記を@post.topic_idに代入
    @post.body = params[:post][:body]
    @topic = Topic.find(@topic_id)
    @post.user_id = current_user.id
    if @post.save
      redirect_to topic_path(@topic_id), notice: '投稿しました'
    else
      render 'posts/new', alert: '投稿できませんでした'
    end
  end
end

ちょっと汚いですね、、、汗
取得したい値は「paramsから取得してインスタンスに代入」と書く形です。
なので、取得したい値に応じて編集してください。上記はあくまで参考として。

viewの修正(おまけ)

以下はトピック一覧画面(topic#index)ですが、postの数やpostの投稿時間を取得して表示することができます。

index.html.erb
<div class="container mt-5 ml-5">
  <div class="row">
      <% @topics.each do |topic| %>
        <div class="table tabel-hover">
          <%= link_to topic do %>
            <div class="list-group" style="max-width: 500px;">
              <div class="list-group-item list-group-item-action">
                <%= topic.title %> (<%= topic.posts.count %>)  #post数を計算
                <br>
                <small class="text-muted text-right">
                最終投稿日時:<%= topic.posts.last.created_at %> #最新のpost投稿時間を取得する
                </small>
              </div>
          <% end %>
          </div>
      <% end %>
      </div>
  </div>
</div>

こんな表示になります↓

スクリーンショット 2020-02-25 1.42.56.png

非常に便利ですね。

参考文献

Active Record の関連付け

2
4
4

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
2
4