自分用のメモです。
Railsで多対多の関係を実装する手順を残しておきます。
モデル
記事投稿の情報をもつPostモデル、
カテゴリの情報を持つCategoryモデル、
その2つを繋ぐ中間テーブルとしてPost_Category_Relationモデルを作成します。
1つのPostは複数のCategoryを持ちえ、1つのCategoryは複数のPostを持つ関係とします。
多対多を実装した経緯
PostとCategoryが "複数 - 複数" 、つまり多対多の関係になっており、
中間テーブルというものを作りデータを管理する必要が出てきたため。
中間テーブルの関係性を表した図
下準備
$ bundle exec rails g model Post user_id:integer title:string content:text category_id:integer
$ bundle exec rails g model Category name:string
$ bundle exec rails g model Post_category_relation post:references category:references
$ bundle exec rails db:migrate(データベースに変更を指示・反映する)
これにより、
・app/modelsフォルダ下に各モデルファイルが生成(app/models/モデル名.rb)
・db/migrateフォルダ下に各マイグレーションファイルが生成
各モデルファイルを編集
Post_category_relationモデルにはbelongs_toが自動で設定されていますが、
PostモデルとCategoryモデルには手動で追加する必要がある。
多対多の形での実装では、中間テーブルとのhas_many関係の記述と、
その中間テーブルを通したPostモデル、Categoryモデルとの紐付けという
2つのhas_many throughを記述する必要がある。
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
class PostCategoryRelation < ApplicationRecord
belongs_to :post
belongs_to :category
end
モデルの実装は以上。
Controllerの実装
新規投稿すると、その投稿の詳細ページに遷移する作りにしています。
投稿に紐付くカテゴリは、投稿を登録する際のパラメータに含まれるため、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, :content, category_ids: [])
end
end
ポイントは、post_paramsメソッドのcategory_ids: []です。
投稿に紐付くカテゴリは、チェックボックスによって複数渡される場合があるため、配列形式であることを記載します。
Viewsの実装
新規投稿する画面です。
前述の多対多のアソシエーションにより、postオブジェクトにcategory_idsというプロパティが追加されます。
<%= form_with model: @post do |f| %>
<p>
<%= f.label :title, 'タイトル' %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content, '本文'%>
<%= f.text_field :content %>
</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 チェックボックスのラベル名
その他
サンプルとして、コンソールからカテゴリ名を登録しておきます。
$ rails cでコンソールを起動して、以下のコードを入力します。
%W[仕事 勉強 家族 恋愛 生活 お金 障害 人間関係 ペット その他].each { |sample| Category.create(name: sample) }
投稿登録後に遷移するviewファイル(投稿の詳細ページ)は以下を使用します。
<p><%= "【タイトル】#{@post.title}" %></p>
<p><%= "【本文】#{@post.content}" %></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 にアクセスして新規投稿画面を表示し、正しく表示されているか確認しておしまい!
▼参考記事▼
大変参考になりました!ありがとうございました!!