LoginSignup
2
3

More than 1 year has passed since last update.

【備忘録】【Rails】多対多の関係の実装(カテゴリ編)

Last updated at Posted at 2021-05-24

自分用のメモです。
Railsで多対多の関係を実装する手順を残しておきます。

モデル

記事投稿の情報をもつPostモデル、
カテゴリの情報を持つCategoryモデル、
その2つを繋ぐ中間テーブルとしてPost_Category_Relationモデルを作成します。

1つのPostは複数のCategoryを持ちえ、1つのCategoryは複数のPostを持つ関係とします。

多対多を実装した経緯

PostとCategoryが "複数 - 複数" 、つまり多対多の関係になっており、
中間テーブルというものを作りデータを管理する必要が出てきたため。

中間テーブルの関係性を表した図

スクリーンショット 2021-05-23 23.22.04.png

下準備

ターミナル
$ 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を記述する必要がある。

app/models/post.rb
class Post < ApplicationRecord
  has_many :post_category_relations
  has_many :categories, through: :post_category_relations
end
app/models/category.rb
class Category < ApplicationRecord
  has_many :post_category_relations
  has_many :posts, through: :post_category_relations
end
app/models/post_category_relation.rb
class PostCategoryRelation < ApplicationRecord
  belongs_to :post
  belongs_to :category
end

モデルの実装は以上。

Controllerの実装

新規投稿すると、その投稿の詳細ページに遷移する作りにしています。
投稿に紐付くカテゴリは、投稿を登録する際のパラメータに含まれるため、strong parametersを使用しています。

app/models/posts_controller.rb
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というプロパティが追加されます。

app/views/posts/new.html.erb
<%= 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ファイル(投稿の詳細ページ)は以下を使用します。

app/views/posts/show.html.erb
<p><%= "【タイトル】#{@post.title}" %></p>
<p><%= "【本文】#{@post.content}" %></p>
<span>【カテゴリ】</span>
<% @post.categories.each do |category| %>
  <%= category.name %>
<% end %>

ルーティングは以下のとおり。

config/routes.rb 
Rails.application.routes.draw do
  resources :posts, only: [:show, :new, :create]
end

最後に

$ rails sでサーバ起動後、
http://localhost:3000/posts/new にアクセスして新規投稿画面を表示し、正しく表示されているか確認しておしまい!

▼参考記事▼

大変参考になりました!ありがとうございました!!

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