現在プログラマとしての転職・起業を視野にRuby/Railsを勉強しています。
今回はRailsでの多対多の関係を実装する際の記録として記事にしました。
モデル
今回はサロンとそのサロンの属するカテゴリを実装する、という形です。サロンの情報をもつSalonモデルとカテゴリの情報を持つCategoryモデル、その2つを繋ぐ中間テーブルとしてClassificationモデルを作成します。
1つのSalonは複数のCategoryを持ちえ、1つのCategoryは複数のSalonを持つ関係とします。
*deviseでSalonの登録ができるようにしている前提で、current_salonを使っています。
多対多を実装した経緯
今回のように1つの商品が複数のカテゴリーを持ち、1つのカテゴリーが複数の商品をもつ実装は1対多(has_many)の形でも実装できますが、『Webエンジニアのためのデータベース技術[実践]入門』も参考に、より綺麗な形での実装のためにhas_many throghを用いた多対多で実装。モデルの作成
```ruby:model $rails g model Salon name:string $rails g model Category name:string ``` モデルを作成後、中間テーブルとなるモデルを作成します。$rails g model Classification salon:references category:references
user:referencesというような形にすることでそれぞれのモデルと紐付けさせます。
こうすることでUserProjectモデルは自動的に以下のようにそれぞれのモデルとの紐付けがなされた状態で作成されます。
class Classification < ApplicationRecord
belongs_to :salon
belongs_to :category
end
マイグレーションは以下のように自動的に外部キーが設定された形で作成されています。
class CreateClassifications < ActiveRecord::Migration[5.2]
def change
create_table :classifications do |t|
t.references :salon, foreign_key: true
t.references :category, foreign_key: true
t.timestamps
end
end
end
Classificationモデルにはbelongs_to
が自動で設定されていますが、ProductモデルとCategoryモデルには手動で追加する必要があります。
多対多の形での実装では、中間テーブルとのhas_many
関係の記述と、その中間テーブルを通したCategoryモデルとの紐付けの2つのhas_many through
を記述する必要があります。
class Salon < ApplicationRecord
has_many :classifications
has_many :categories, through: :classifications
end
class Category < ApplicationRecord
has_many :classifications
has_many :salons, through: :classifications
end
モデルの実装は以上。
Controllerの実装
中間テーブルにもデータを保存するためのコードを書いていきます。 今回はSalon作成後、そのSalonが属するカテゴリを登録する形で実装します。class CategorysController < ApplicationController
def create
@salon = current_salon
@category = @salon.categoies.build(category_params)
if @salon.save
flash[:success] = "カテゴリーを登録しました"
redirect_to root_url
else
flash[:danger] = "カテゴリーが登録できませんでした"
redirect_to root_url
end
end
private
def category_params
params.require(:category).permit(:name)
end
end
Controllerの実装は以上です。
Viewsの実装
カテゴリーは今回は好きなカテゴリーを追加できるような形での実装にし、form_forではtext_fieldを使って登録するようにしました。 また、ログインした後はトップページにユーザーページを表示させ、そこにform_forを設置しています。<%= form_for(current_salon.categories.build) do |f| %>
<%= f.label :"カテゴリー登録" %>
<%= f.text_field :name, placeholder: "Category Name" %>
<%= f.submit "Registrate" %>
<% end %>
特定のSalonのカテゴリー名を全て表示させるような場合には以下のように記述します。
<% @salon.categoies.each do |category| %>
<%= category.name %>
<% end %>
とりあえずはこのくらいで。