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へ続く。