1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby on Rails初心者の学習記録 Part5:復習

Last updated at Posted at 2023-02-05

はじめに

Ruby on Rails初心者の学習記録Part5です。
今までRubyの基礎文法から始まり、RailsにおけるMVCやCRUDについて学んできました。
今回は、"Getting Started with Rails""8 Adding a Second Model" を教材にコメント機能の実装を通して、今まで学んできたことを復習します。

1. モデルの作成

Part2で記事データを扱うために作成したArticleモデルのようにコメントデータを扱うCommentモデルを作成します。Commentモデルには、記事への参照も追加します。

rails generate model Comment commenter:string body:text article:references

実行後、以下4つのファイルが作成されます。
image.png
Commentモデルのファイルは以下のようになっています。

app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :article
end

モデル作成時のコマンド、記事への参照(article:references)を追加したので、belongs_to :articleが記載されています。これにより Active Recordアソシエーション(関連づけ) というものが設定されます(詳細は次章で確認)。
また、:referencesはモデルの特殊なデータ型を表しています。指定されたモデル名の後ろに_idを追加した名前を持つカラムをデータベース内に作成します(マイグレーション後、実際にdb/schema.rbで確認)。

また、マイグレーションファイルはPart2で確認したときと同じようにデータベーステーブルと一致するように作成されています。

db/migrate/20230201002601_create_comments.rb
class CreateComments < ActiveRecord::Migration[7.0]
  def change
    create_table :comments do |t|
      t.string :commenter
      t.text :body
      t.references :article, null: false, foreign_key: true

      t.timestamps
    end
  end
end

t.referencesでは、article_idというinteger型のカラムとそのインデックス、articlesテーブルのidカラムを指す外部キー制約を作成します。
それでは、実際にマイグレーションを実行します。

rails db:migrate

Railsは現在のデータベースに対して、今まで実行されていないマイグレーションのみを実行します。そのため、実行結果のメッセージは、今回作成したcommentsのみ表示されます。
image.png

そして、db/schema.rbには、commentsが追加されました。

db/schema.rb
ActiveRecord::Schema[7.0].define(version: 2023_02_01_002601) do
  create_table "articles", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "comments", force: :cascade do |t|
    t.string "commenter"
    t.text "body"
    t.integer "article_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["article_id"], name: "index_comments_on_article_id"
  end

  add_foreign_key "comments", "articles"
end

2. モデルのアソシエーション(関連づけ)

次に先ほどのモデル追加時に出てきたActive Recordアソシエーションについてみていきます。この機能により、2つのモデル間にリレーションを宣言することができます。今回の記事とコメントというケースでは、以下のどちらかの方法でリレーションを設定できます。

  • Each comment belongs to one article.
  • One article can have many comments.

この方法は、Railsがアソシエーションを宣言する方法と似ています。例えば、先ほど作成したCommentモデルはコメントが1つの記事に紐づいています。

app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :article
end

なお、この時点ではCommentモデル側のみアソシエーションがなされています。そのため、Articleモデル側も対応する必要があります。

app/models/article.rb
class Article < ApplicationRecord
  has_many :comments

  validates :title, presence: true
  validates:body, presence: true, length: { minimum: 10 }
end

これら2つの宣言により、かなりの動作が自動化されます。例えば、記事が1件含まれている@articleというインスタンス変数があれば、@article.commentsと書くことでその記事に紐づいている全てのコメントを取得することができます。

より詳しいActive Recordアソシエーションの説明は、"Active Record Associations"に記載があります。

3. コメントのルーティング

Part1Part3articlesコントローラーに対して行ったときと同様、ルーティングを追加する必要があります。config/routes.rbcomments用のルーティングを追加します。

config/routes.rb
Rails.application.routes.draw do
  root "articles#index"
  
  resources :articles do
    resources :comments
  end
end

ここでは、commentsarticles内にネストされたリソースとして作成しています。これは、モデルの記述とは別の観点から記事とコメントのリレーションシップを階層的に捉えたものです。

4. コントローラーの作成

モデルを作成できたので、次はコントローラーを作成します。
Part1のときと同じように以下のコマンドで作成します。

rails generate controller Comments

実行後、以下のファイルが作成されます。
image.png
この状態だと記事とコメントは別画面になります。ただ、一般的なブログは記事とコメントが同じ画面であることが多いと思います。
そのため、まずは記事表示画面(app/views/articles/show.html.erb)に新たなコメント作成欄を追加します。

app/views/articles/show.html.erb
<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
  <li><%= link_to "Destroy", article_path(@article), data: {
    turbo_method: :delete,
    turbo_confirm: "Are you sure?"
  } %></li>
</ul>

<h2>Add a comment:</h2>
<%= form_with model: [ @article, @article.comments.build ] do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <P>
    <%= form.submit %>
  </p>
<% end %>

ここで追加されたフォームは、CommentsControllercreateアクションを呼び出すことでコメントを新規作成します。
また、form_withには配列を渡し、/articles/1/commentsのようなネストしたルーティングをビルドします。
image.png
次はapp/controllers/comments_controller.rbcreateアクションを作成し、実際に先ほど作成したフォームから呼び出せるようにします。

app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  private
    def comment_params
      params.require(:comment).permit(:commenter, :body)
    end
end

記事のコントローラーより少し複雑になっています。これはネストを使ったことによる影響です。コメントのリクエストでは、コメントを記事に紐づける必要があります。そのため、Articleモデルのfindを最初に呼び、リクエストに含まれている記事を取得します。
加えて、このコードではアソシエーションにより利用できるようになったメソッドをいくつか利用しています。@article.commentsでは、コメントを保存するためにcreateを利用しています。これにより、コメントが記事と自動的に紐づき。記事に対してコメントが従属するようになります。

次は作成したコメントを画面上に表示できるようにします。
新しいコメントを作成すると、article_path(@article)ヘルパーを利用してユーザーは元の記事の画面に戻ります。このヘルパーを呼び出すとArticlesControllershowアクションが呼び出され、記事画面(show.html.erb)がレンダリングされます。そのため、この画面にコメントを作成したコメントを表示できるようにします。

app/views/articles/show.html.erb
<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
  <li><%= link_to "Destroy", article_path(@article), data: {
    turbo_method: :delete,
    turbo_confirm: "Are you sure?"
  } %></li>
</ul>

<h2>Comments</h2>
<% @article.comments.each do |comment| %>
  <p>
    <strong>Commenter:</strong>
    <%= comment.commenter %>
  </p>

  <p>
    <strong>Comment:</strong>
    <%= comment.body %>
  </p>
<% end %>

<h2>Add a comment:</h2>
<%= form_with model: [ @article, @article.comments.build ] do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <P>
    <%= form.submit %>
  </p>
<% end %>

これでコメント機能完成です!
ezgif-5-b0bdbf441d.gif

最後に

今回はコメント機能の実装を通して、モデルやルーティング、コントローラーの作成について復習しました。また、新たにモデルのアソシエーションについても学習しました。
最後までお読みいただきありがとうございました!

参考

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?