Rails 掲示板にコメント機能を追加 part1

概要

  • 掲示板詳細画面を追加し、その中でコメントをできるようにする。
  • 掲示板一覧画面から掲示板のタイトル名を押すと掲示板詳細画面へ行けるようにする。
  • 書き込んだコメントが一番上に表示されるようにコメントの並び順を指定
  • コメントした本人のみに削除・編集ボタンを表示させる
  • コメントを投稿したユーザーが削除されると、ユーザーが投稿したコメントも削除される
  • コメントが投稿された掲示板が削除されると、掲示板に投稿されたコメントも削除される

モデルの作成

Commentモデルを作成し、User、Boardモデルとアソシエーションを定義する。
Commentモデルに外部キー(user_id, board_id)を設定。
Commentモデルにバリデーションを設定。
今回はreferencesでモデルと同時に作成したのでCommentモデル側のアソシエーションは自動で定義される。
(CommentをUserとBoardに関連付けたいので、User、Boardにhas_many、Commentにbelong_toを定義)

$ rails g model Comment body:text user:references board:references
      invoke  active_record
      create    db/migrate/20210522131533_create_comments.rb
      create    app/models/comment.rb

空のデータは登録できないようにNOTNULL制約を追加。
外部キーが記述されていることを確認する。

class CreateComments < ActiveRecord::Migration[5.2]
  def change
    create_table :comments do |t|
      t.text :body, null: false
      t.references :user, foreign_key: true
      t.references :board, foreign_key: true

      t.timestamps
    end
  end
end

ここまで確認できたらrails db:migrateを実行。

== 20210522131533 CreateComments: migrating ===================================
-- create_table(:comments)
   -> 0.0036s
== 20210522131533 CreateComments: migrated (0.0040s) ==========================

Commnetモデルにバリデーションの追加。
空の投稿を防ぐ為、presence: trueをつける。

class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :board

  validates :body, presence: true, length: { maximum: 10_000 }
end

User、Boardモデル側に以下のアソシエーションを定義
has_many :comments, dependent: :destroy
dependent: :destroyオプションを定義しないと、親モデル(user board)を削除した時に、その親モデルに紐づく子モデル(comment)の内容が残ってしまうので注意する。

掲示板詳細画面の作成

ルーティングの作成

今回は、BoardモデルとCommentモデルが紐付いてるので、ルーティングをネスト(入れ子)する必要がある。
ネストする場合はshallowオプションを使用する。
shallowオプションを使用するとルーティングが簡潔になる。

  resources :boards, only: %i[index new create show] do
    resources :comments, only: %i[create destroy], shallow: true
  end

使用前
comments#createはcommentのidではなくboard_idを指定する。

        Prefix Verb   URI Pattern                              Controller#Action
board_comments POST   /boards/:board_id/comments(.:format)     comments#create
 board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy

使用後
comments#destroyはcommentのidを指定するのでboard_idは指定しない。

        Prefix Verb   URI Pattern                          Controller#Action
board_comments POST   /boards/:board_id/comments(.:format) comments#create
       comment DELETE /comments/:id(.:format)              comments#destroy

controllerの作成

@board = Board.find(params[:id]) でリクエストパラメータ(params)経由でidを受け取りBoardオブジェクトをデータベースから取得。
findはidによってそのモデルに対応するレコードをデータベースから検索する。
params[:id]にはURL “localhost:3000/boards/[id]”の[id]の部分が格納されてる。

class BoardsController < ApplicationController
  skip_before_action :require_login, only: %i[index create show]
#...
  def show
    @board = Board.find(params[:id])
#...

viewの作成

boards/show.html.erbを作成。

#...
<%= image_tag @board.board_image.url, class: 'card-img-top img-fluid', size: '300x200' %>
<%= @board.title %>
#掲示板名の表示
<%= @board.user.decorate.full_name %>
#user_decorator.rbのfull_nameメソッドを呼び出し、ユーザー名を表示する。
<%= l @board.created_at, format: :long %>
#...

lメソッドについて

lメソッドはi18nにおいて日付や時刻を表す際に使用する。
config/locales/ja.ymlに書かれた内容を読み込む。
formatオプションを使うことで複数の書式を使い分けることができる。

ja:
  time:
    formats:
      default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z"
      # 2021年05月23日(日) 02時10分39秒 +0900
      long: "%Y/%m/%d %H:%M"
      # 2021/05/23 02:10
      short: "%m/%d %H:%M"
      # 05/23 02:10

掲示板一覧画面から掲示板のタイトル名を押すと掲示板詳細画面へ行けるようにする。

掲示板一覧画面に詳細画面へのリンクを追加。
今回のboard_pathはidが指定されているのでlink_to prefix_path(渡すid)と記述。

   <%= link_to board.title, board_path(board.id) %>

part2へ続く。