###前提
記事に対してコメントを投稿・詳細表示・一覧表示・編集・削除するやり方についての続きとなります。
すでに作成してある記事詳細機能の箇所にコメント投稿欄、一覧表示、削除機能を加えていきます。
###ルーティング作成
まず最初に、必要なルーティングを作成していきます。
Rails.application.routes.draw do
・
・
・
resources :articles do
resources :comments, only: [:create, :destroy]
end
end
今回は、articleとcommentの関係性が「article:comment = 1:多(has_manyとbelongs_toの関係)」なので入れ子でルーティングを作成します。また、createアクションとdestroyアクション以外は今回使わない予定なので、only:
をつけて制限しています。
一旦、正しくルーティングが反映されているか確認してみましょう。
$ rails routes
Prefix Verb URI Pattern Controller#Action
article_comments POST /articles/:article_id/comments(.:format) comments#create #コメント投稿
article_comment DELETE /articles/:article_id/comments/:id(.:format) comments#destroy #コメント削除
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
commentのcreateとdestroyのみルーティングが作成されていることや既存のarticleのルーティングが変化していないことが確認できました。
###モデルの作成
モデルを作成していきます。
今回のコメントテーブルにも、titleカラムとcontentカラムを用意していきます。
$ rails g model Comment title:string content:text article:references
Running via Spring preloader in process 13297
invoke active_record
create db/migrate/20190702071230_create_comments.rb
create app/models/comment.rb
invoke test_unit
create test/models/comment_test.rb
create test/fixtures/comments.yml
article:references
を追加することで「commentsテーブルに外部キーを設定すること」と「commentモデルにbelongs_to :article」が設定されます。
class Comment < ApplicationRecord
belongs_to :article
end
class CreateComments < ActiveRecord::Migration[5.0]
def change
create_table :comments do |t|
t.string :title
t.text :content
t.references :article, foreign_key: true
t.timestamps
end
end
end
問題なさそうなのでマイグレーションを実行していきます。
$ rails db:migrate
== 20190702071230 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0056s
== 20190702071230 CreateComments: migrated (0.0061s) ==========================
ActiveRecord::Schema.define(version: 20190702071230) do
・
・
・
create_table "comments", force: :cascade do |t|
t.string "title"
t.text "content"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["article_id"], name: "index_comments_on_article_id"
end
・
・
・
end
###モデル間でリレーションを作成する
articleとcommentの関係性
- **1つのarticle(記事)は複数のcomment(コメント)**を持つこどができる→has_many
- **1つのcomment(コメント)は1つのarticle(記事)**に属することができる→belongs_to
この関係性を各モデル間で表現すると以下のようになります。
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
end
dependent: :destroy
を加えることで記事を削除したら関連するコメントも自動で削除されます。
class Comment < ApplicationRecord
belongs_to :article
end
###コントローラー作成
続いてはcommentsコントローラーを作成していきます。
rails g controller Comments
Running via Spring preloader in process 8903
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
invoke test_unit
create test/controllers/comments_controller_test.rb
invoke helper
create app/helpers/comments_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/comments.coffee
invoke scss
create app/assets/stylesheets/comments.scss
articlesコントローラーとcommentsコントローラーにそれぞれ必要な処理を実装していきます。
class ArticlesController < ApplicationController
・
・
・
def show
@article = Article.find(params[:id])
#コメントフォームに必要なインスタンスを生成
@comment = Comment.new
end
・
・
・
class CommentsController < ApplicationController
def create
#コメントを投稿する対象のインスタンス(記事)を作成
@article = Article.find(params[:article_id])
#buildメソッドを用いることでcommentインスタンスにarticle_idをセットしてcommentインスタンスを作成
@comment = @article.comments.build(comment_params)
if @comment.save
redirect_to @article
end
end
def destroy
#記事(article)の情報を取得
@article = Article.find(params[:article_id])
# その記事内の削除対象のコメントを探して取得
@comment = @article.comments.find(params[:id])
if @comment.destroy
redirect_to article_path(@article)
end
end
private
def comment_params
params.require(:comment).permit(:title,:content)
end
end
buildメソッドは「インスタンスの作成+外部キーを設定」までやってくれますが、DBに保存まではやってくれないので、saveメソッドを使って保存する必要があります。
###ビューの作成
記事詳細画面にコメントの入力フォームやコメントの一覧を表示していきます。
<table>
<tbody>
<tr>
<th>タイトル</th>
<td><%= @article.title %></td>
</tr>
<tr>
<th>内容</th>
<td><%= @article.content %></td>
</tr>
</tbody>
</table>
<h2>コメントを入力</h2>
<!--引数の順番は親、子-->
<%= form_for ([@article,@comment]) do |f| %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %><br/>
</p>
<p>
<%= f.label :content %><br/>
<%= f.text_area :content %><br/>
</p>
<p>
<%= f.submit "送信" %>
</p>
<%end%>
<h2>コメント一覧</h2>
<!--@article.commentsで該当する記事のコメント一覧を取得-->
<% @article.comments.each do |comment| %>
<p>タイトル</p>
<p><%= comment.title %></p>
<p>内容</p>
<p><%= comment.content %></p>
<!--article_comment_pathヘルパーには親(article)と子(comment)を両方渡す-->
<p><%= link_to '削除',article_comment_path(@article,comment),
method: :delete,data: {confirm:'削除してもよろしいですか?'}%></p>
<% end %>
内容が少し冗長なのでパーシャルを用いて
入力フォームとコメント一覧の箇所をリファクタリングしていきます。
<h2>コメント一覧</h2>
<!--@article.commentsで該当する記事のコメント一覧を取得-->
<% @article.comments.each do |comment| %>
<p>タイトル</p>
<p><%= comment.title %></p>
<p>内容</p>
<p><%= comment.content %></p>
<!--article_comment_pathヘルパーには親(article)と子(comment)を両方渡す-->
<p><%= link_to '削除',article_comment_path(@article,comment),
method: :delete,data: {confirm:'削除してもよろしいですか?'}%></p>
<% end %>
<h2>コメントを入力</h2>
<!--引数の順番は親、子-->
<%= form_for ([@article,@comment]) do |f| %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %><br/>
</p>
<p>
<%= f.label :content %><br/>
<%= f.text_area :content %><br/>
</p>
<p>
<%= f.submit "送信" %>
</p>
<%end%>
<table>
<tbody>
<tr>
<th>タイトル</th>
<td><%= @article.title %></td>
</tr>
<tr>
<th>内容</th>
<td><%= @article.content %></td>
</tr>
</tbody>
</table>
<%= render @article.comments %>
<%= render "comments/form" %>
最後に
誤っている箇所や追記した方が良い点等ございましたら
編集リクエストやコメントの方でご指摘していただけると幸いです。
参考文献
Railsを始めたばかりの人向け!Railsの仕組みを一から理解しながらブログを作成する
Ajaxを用いた動的なコメント投稿・削除機能の実装で学ぶRuby on Rails
buildモデルを生成(new/build)