Closed fukasawamoe closed 9 months ago
[x] stepモデルのaction_1のカラムにアクセスできない
[x] そもそも、stepモデルを取得するのにgoal.idを通さないといけないのが不便なので改修 →user.idをとるように変更
[x] goalモデルとstepモデルは現状1:1(stepの中にカラムで分かれているのでデータ抽出が面倒)なので1:多に改修 → 同一モデルの異なるレコードを複数作成し、同時にcreateする editでfields_forを使用 →goals_controllerに書かなければいけない goalのフォームも別に存在するので無理
フォームオブジェクト使えるのかよくわからなかった
[ ] FormObject使うしかない...?
[ ] 達成状況のところ・曜日のところのajax処理
[x] auto login
進捗ダメそうです
fields_forでゴリ押しでなんとかなりました edit_multipleってeditじゃダメなんでしょうか...?(GPTに聞いたら役割が違うよ、とのことでしたが)
「form_withで@.stepを扱っていいの?その場合はfields_forは不要なのでは?」というのはとても良い質問です。 まず、 form_with model: @.stepの部分は、formがどのモデルに対して動作すべきかをRailsに伝えます。しかし、現在はそれを一つのモデルのための単一フォームとして扱うのではなく、複数のStepインスタンスを更新するフォームとして使用しています。 一方、fields_forはフォーム内の特定の部分が特定のモデルオブジェクトに属することを示します。つまり、form_with model: @.stepという行は無視され、 fields_for "steps[#{step.id}]", stepが各ステップを適切に処理します。 したがって、form_withで@.stepというモデルを指定する代わりに、単にform_with url: update_multiple_step_path, method: :put,local: true と書くことができます。これにより、formは任意のURLに対してPUTメソッドのリクエストを送信します。そして、 fields_forは各個別のStepオブジェクトに対して独立したパラメータセットを作成します。
とのこと、よってform_withの@.stepは削除。
これでstep_idでデータを引っ張って来れるようになったのでやっとachievementに取り掛かります
やっとachievementモデルに行きつきました。Stepモデル作成時と登録skip時の両方でAchievementモデルを作成する挙動は実装できました。 その中でのコードでhas_oneのアソシエーション時に使えるメソッドを初めて知りました
@.step.create_achievement! は Rails の has_one アソシエーションに対して提供されるメソッドの一部です。アソシエーションとは、モデル間のリレーションシップを定義するもので、この場合は Step モデルと Achievement モデルの間に has_one リレーションシップを定義しています。 これにより、ある Step インスタンスに対して、.create_achievement または .create_achievement! といったメソッドを用いて関連する Achievement インスタンスを作成することができます。これは Achievement.new を使ってインスタンスを作成し、それを保存するよりも簡潔に行うことができます。 create_achievement と createachievement! の違いは、前者は作成に失敗したときに false を返し、後者は例外 ActiveRecord::RecordInvalid をスローするところです。 このメソッドの名前は自動的に生成され、基本的にはモデル名の create というプレフィックスから成ります。したがって、 @.step.create_achievement! は @.step に関連する Achievement を作成し、それがうまくいかない場合は例外をスローするようにします。 このように、Rails ではアソシエーションを使ってモデル間の関係を効率的に管理することができ、コードも読みやすく保つことができます。素晴らしい点ですね!
引き続きボタンの実装からやっていきます
home/index.html.erb
<%= render template: "goals/show", locals: { goal: @goal, steps: @steps } %>
goals/show.html.erb
<% steps.each do |step| %>
<% action = step.action %>
<% times_set = step.times_set %>
<% unless action.blank? && times_set.blank? %>
<li><i class="fa-solid fa-fire" style="color: #f26845;"></i> <%= action %>を週<%= times_set %>回行う</li>
<div class='flex flex-wrap'>
<% (0..6).each do |day| %>
<% if step.achievement.day_check.include?(day) %>
<%= render "achievements/on_button", day: day, step: step %>
<% else %>
<%= render "achievements/off_button", day: day, step: step %>
<% end %>
<% end %>
</div>
<% all_blank = false %>
<% end %>
<% end %>
achievement/_off_button.html.erb
<div>
<%= button_to t('date.abbr_day_names')[day], add_day_check_step_path(step.achievement, day: day), method: :post, class:"mx-auto m-3 bg-white border-2 py-2 px-6 rounded-full text-lg" %>
</div>
のようになっている場合はstepのスコープ内なのでsteps_controllerにロジックを書く。 そしてボタンの送信先もstepのpath宛てにする。