はじめに
Ruby on Railsで、投稿(Post)とカテゴリ(Category)の紐付けを実装します。
モデルやコントローラの生成は割愛します。予めご了承ください。
実行環境
- Ruby 2.5.1
- Rails 5.2.3
完成イメージ
1.投稿に紐付けるカテゴリは、チェックボックスから複数選択できます。
2.「投稿する」ボタンをクリックすると、DBへの登録とページ遷移が行われます。
多対多のアソシエーション(関連付け)
Post
は複数のCategory
に紐付き、Category
も複数のPost
に紐付きます。これを多対多の関係といいます。
多対多の関係は、中間テーブルを利用して実現します。
中央のPost_category_relations
が中間テーブルです。紐付けるモデルのpost_id
とcategory_id
を持ちます。
モデル
まずは中間テーブルのモデルから。
Post
とCategory
をbelongs_to
で関連付けます。
class PostCategoryRelation < ApplicationRecord
belongs_to :post
belongs_to :category
end
続いて、Post
とCategory
のモデルです。
中間テーブルのモデルであるpost_category_relations
をhas_many
で関連付けます。
class Post < ApplicationRecord
has_many :post_category_relations
has_many :categories, through: :post_category_relations
end
class Category < ApplicationRecord
has_many :post_category_relations
has_many :posts, through: :post_category_relations
end
また、Post
はCategory
を、Category
はPost
をhas_many
で関連付けます。
ここでポイントとなるのが、**through: :post_category_relations
**です。
中間テーブルのpost_category_relations
を経由して、関連付けをすることを意味しています。
なお、has_many
の関連付けは、中間テーブルを先に記述しないとActiveRecord::HasManyThroughOrderError
というエラーが発生しますので、注意してください。
ビュー
投稿を登録する画面です。
前述の多対多のアソシエーションにより、post
オブジェクトにcategory_ids
というプロパティが追加されます。
<%= form_with model: @post do |f| %>
<p>
<%= f.label :title, '題目' %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body, '本文'%>
<%= f.text_field :body %>
</p>
<p>
<%= f.label :category, 'カテゴリ' %>
<%= f.collection_check_boxes(:category_ids, Category.all, :id, :name) do |category| %>
<%= category.label do %>
<%= category.check_box %>
<%= category.text %>
<% end %>
<% end %>
</p>
<%= f.submit '投稿する' %>
<% end %>
存在するカテゴリの数だけチェックボックスを作成するために、collection_check_boxes
メソッドを使用します。引数の内容は以下のとおりです。
- 第一引数
:category_ids
post
オブジェクトのプロパティ名 - 第二引数
Category.all
category
オブジェクトの配列を取得 - 第三引数
:id
チェックボックスのvalue属性の値 - 第四引数
:name
チェックボックスのラベル名
コントローラ
今回は投稿を登録すると、その投稿の詳細ページに遷移する作りにしています。
投稿に紐付くカテゴリは、投稿を登録する際のパラメータに含まれるため、strong parametersを使用しています。
class PostsController < ApplicationController
def new
@post = Post.new
end
def create
@post = Post.create(post_params)
redirect_to @post
end
def show
@post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:title, :body, category_ids: [])
end
end
ポイントは、post_params
メソッドのcategory_ids: []
です。
投稿に紐付くカテゴリは、チェックボックスによって複数渡される場合があるため、配列形式であることを記載します。
その他
サンプルとして、コンソールからカテゴリ名を登録しておきます。
$ rails c
でコンソールを起動して、以下のコードを入力します。
%W[Ruby Java Python Go].each { |sample| Category.create(name: sample) }
投稿登録後に遷移するviewファイル(投稿の詳細ページ)は以下を使用します。
<p><%= "【題目】#{@post.title}" %></p>
<p><%= "【本文】#{@post.body}" %></p>
<span>【カテゴリ】</span>
<% @post.categories.each do |category| %>
<%= category.name %>
<% end %>
ルーティングは以下のとおり。
Rails.application.routes.draw do
resources :posts, only: [:show, :new, :create]
end
#終わりに
$ rails s
でサーバ起動後、http://localhost:3000/posts/new
にアクセスして投稿登録画面を表示します。
完成イメージ通りに、投稿とカテゴリを紐付けた登録ができるでしょう👏