LoginSignup
2
0

More than 1 year has passed since last update.

【Ruby on Rails】コメント機能を初心者なりにまとめてみた(非同期)

Posted at

対象者

  • コメント機能を実装予定の方
  • 非同期通信(Rails×Ajax)を実装予定の方

目的

  • 記事へのコメントを非同期で投稿・削除ができるようにする

実際の手順と実例

1.前提

  • jQueryの読み込みができている
  • バージョン:Ruby:2.3.0、Rails:5.2.5です。
  • Articleモデル、Userモデル実装済み

2.モデルの作成とテーブル設定

まずコメントモデルを作成します。
UserとArticleとアソシエーションを組みます。

$ rails g model comment user:references article:references

その後、config/db/schema.rbにマイグレーションファイルが作成されているので、下記を追記していきます。

class CreateComments < ActiveRecord::Migration[5.2]
  def change
    create_table :comments do |t|
      t.text :comment_content #追記
      t.references :user
      t.references :article, foreign_key: true

      t.timestamps
    end
  end
end

上記を確認して、rails db:migrateを実行すると
schemaファイルに下記が作成されます。

scheme.rb
  create_table "comments", force: :cascade do |t|
    t.text "comment_content"
    t.integer "user_id"
    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"
    t.index ["user_id"], name: "index_comments_on_user_id"
  end

その後各モデルを確認します。

app/model/comment.rb
  belongs_to :user
  belongs_to :post
  validates :comment_content, presence: true

Commentがないと投稿できないようにバリデーションを設定しました。

app/model/user.rb
  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy
app/model/article.rb
  belongs_to :user
  has_many :comments, dependent: :destroy

※ dependent: :destroyでアソシエーションを組んでるものが削除されたとき、それに合わせて削除されるように設定

User:Comment = 1:N
ユーザー1人に対してたくさんの投稿を持てる

Article:Comment = 1:N
記事1つに対してたくさんの投稿を持てる

3.コントローラーとルーティングの設定

ルーティングはネストして設定します

route.rb
  resources :posts do
    resources :comments
  end

下記コマンドでController作成します。

$ rails g controller comment

出来上がったControllerに下記を追加していきます。

app/comments_controller.rb
class CommentsController < ApplicationController

  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.build(comment_params) #①
    @comment.user_id = current_user.id
    if @comment.save
     redirect_to request.referer #②
    else
     redirect_to request.referer
    end
  end

  def destroy
    @comment = Comment.find(params[:id])
    if @comment.destroy
     redirect_to request.referer
    else
     redirect_to request.referer
    end
  end

  private

  def comment_params
    params.require(:comment).permit(:comment_content, :article_id, :user_id)
  end

end

① .buildメソッドとは親モデルに属する子モデルのインスタンスを新たに生成したい場合に使うメソッドです。外部キーに値が入った状態でインスタンスが生成できます。

②元のページにリダイレクトしてくれます。

4.投稿のControllerとViewの設定

今回はarticleに対してCommentを残すので、
articles_controllerに記載します。

app/controller/articles_controller.rb
  def show
    @comment = Comment.new
    @comments = @article.comments
  end
app/views/articles/show.html.erb
  <div id = "comment_area"> #①
      <%= render "comments/form", comment: @comment %>
  </div>

  <div>
     <%= render "comments/index", comments: @comments, article: @article %>
  </div>

①のidをターゲットにして、このdiv内をAjaxで書き換えます。(後述)

また、部分テンプレートを使用して、投稿フォームと投稿一覧を同じページに表示します。その2つのViewを作成して、下記の内容を追記します。

app/views/comments/_form.html.erb
<div class="form-group row">
  <%= form_with(model: [@article, @comment]) do |f| %>

    <%= f.label :comment_content,"コメント", class:"form-label" %>

    <%= f.text_area :comment_content %>

    <%= button_tag type: "submit", class: "btn btn-default" do %>
      <i class="far fa-comment-alt"></i>
    <% end %> #①

<% end %>

①ではFontAwesomeを使用したのでこのような表記になっています。

app/views/comments/_index.html.erb
<% @comments.each do |comment| %>
  <p><%= comment.comment_content %></p>
  <% if comment.user == current_user %>
      <p><%= link_to  "Destroy", article_comment_path(comment.article_id, comment.id), method: :delete, remote: true %></p>
  <% end %>
<% end %>
  • ログインしているユーザーだけがCommentを削除できるようにif 文を使ってます。
  • remote: trueで非同期通信に対応しています。

5.非同期通信の設定

今回はcreateとdestroyを非同期設定していきます。

app/views/commentsのフォルダ内に下記2つのjsファイルを作成し、追記します。

app/views/comments/create.js.erb
$("#comments_area").html("<%= j( render "comments/form", comment: @comment ) %>")
$("textarea").val('')
app/views/comments/destroy.js.erb
$("#comments_area").html("<%= j( render "comments/form", comment: @comment ) %>")
$("textarea").val('')

両方同じ内容です。
$("textarea").val('')の記述は、コメント入力後のコメント入力欄を空にしています。

これで非同期のComment実装完了です!!

参照

Ajaxを用いた動的なコメント投稿・削除機能の実装で学ぶRuby on Rails
buildメソッドについて

投稿者コメント

ここまで長い記事初めて書きました。
結構たいへんでしたが、自分で書いていてかなり理解を深めることができたのではないかと思います。

My Profile

プログラミング学習歴3ヶ月目のアカウントです!
プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。
また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。
もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。 

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