実現したかったこと
deviseのアカウント登録の際に2つのモデルに記録させたい。
(ユーザー情報と会社情報)
どうして?
下記の問題が起こるため。
- Followテーブル
CampanyとUserそれぞれで User Aさんをフォローしたかった。
問題箇所
フォロー機能実装の際にCampany(モデル)とUser(モデル)で登録したユーザーは同じ番号のidを持っている。
┗Campanyページからアカウント登録を行った際の1番目のユーザーidは1番。
┗Userページからアカウントアカウント登録を行った際の1番目のユーザーidは1番。
Followテーブルにfollowerとして情報を記録する際に後々の紐付けのためにidを残したいがCampanyとUserはそれぞれ違うテーブルから来たid情報。
id 1となり重複してしますので、この方法は良くなかった。
考えた解決方法
法人(Campany)もUserとみなしUserテーブルに登録する。
ただし法人情報は子モデルとしてCampaniesテーブルに登録する。
準備
deviseのデフォルト設定カラムに任意カラム追加
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up,
keys: [:name, :category_id, campany_attributes: [:campany_name, :campany_logo, :category_id, :campany_url, :tel, :staff_last_name_kana, :staff_first_name_kana]])
end
campany_attributes: [:campanyレコードのカラム名, :campanyレコードのカラム名, ]
モデル名_attributesとすることで親モデルに紐づけている。
(補足:campany_attributesのcampanyはhas_one関係なら単数、has_manyなら複数)
アソシエーション設定
# userモデルの中でcampanyモデルへも同時書き込み
has_one :campany, inverse_of: :user
accepts_nested_attributes_for :campany
# Userモデル中でcampanyモデルへも書き込む
belongs_to :user, inverse_of: :campany
今回はnewアクションはuser登録で使用している。
法人登録は別画面にしたかったので下記のルートで準備。
get '/campany/new' => 'users/registrations#campany' ,as: :new_campany
参考にした記事では
@user.campany.buildでネストモデルの構造にできるとあったが、この記述だがうまく行かなかった。
下記の記法にすることで解決できた。
またcreateアクションはdeviseの機能を上書きするのでsuper以下に記述する。
最後にuser.saveとしなければ子モデル(campany)には保存されないので忘れずに。
# 企業の登録フォーム
def campany
@user = User.new
@user.build_campany
end
def create
super
user = User.new(configure_sign_up_params)
user.save
end
注意:
子モデルへのデータの渡し方は下記のようにf.fields_forを使用する。
上下のようにインデントがきちんと整っていれば実現したいレイアウトに合わせてUserモデル、Campanyモデルへ渡すデータの位置がバラバラに置いても良い。
(親formはform_forであること)
<h2><%= t('.sign_up') %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label "会社名" %><br />
<%= f.text_field :name, autocomplete: "off" %>
</div>
<%= f.fields_for :campany do |campany| %>
<div class="field">
<%= campany.label "会社URL" %><br />
<%= campany.text_field :campany_url, autocomplete: "off" %>
</div>
<div class="field">
<%= campany.label :tel %><br />
<%= campany.telephone_field :tel, autocomplete: "off" %>
</div>
<% end %>
<div class="field">
<%= f.label "カテゴリー" %><br />
<%= f.collection_select :category_id, Category.all, :id, :name, {prompt: "選択してください"}%>
</div>
<%= f.fields_for :campany do |campany| %>
<div class="field">
<%= campany.label "ご担当者様 姓(カナ)" %><br />
<%= campany.text_field :staff_last_name_kana, autocomplete: "off" %>
</div>
<div class="field">
<%= campany.label "ご担当者様 名(カナ)" %><br />
<%= campany.text_field :staff_first_name_kana, autocomplete: "off" %>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="actions">
<%= f.submit t('.sign_up')%>
</div>
<% end %>
<%= render "devise/shared/links" %>
無事に2つのモデルに保存されました。
失敗したこと
registrations_controllerの中でcampanyメソッドを最初はnew_campanyという名前にしていた。
エラーで**new_campany_path?**と言われたがそんなはずはない。
そもそもメソッド名がprefixパスでエラー文吐き出すってどういう意味?
悩むこと3時間、予約語 のキーワードが頭をよぎった。
まさかと思いメソッド名を変えたところビューに無事飛んでくれた。
予約語には気をつけよう。