🍣

演算子 " << "を使ってデータを保存する

2022/12/04に公開

いいね機能の実装中、モデルにインスタンスメソッドを定義してて詰まりました。
備忘録です。

前提

Restaurantモデルに、いいねみたいな行ったよのマークが欲しい。
どのUserが、 どのRestaurantにマークを付けるのかという、
多対多の関連付けを実現するために中間モデルを作成した。
usersモデルrestaurantsモデルの中間モデルをvisitsとして設定した。
https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1#多対多mnを設計しよう

演算子 "<<"を使ってデータを保存

Active Recordの関連付けについて、Railsガイドより

4.3.1.2 collection<<(object, ...)
collection<<メソッドは、1つ以上のオブジェクトをコレクションに追加します。このとき、追加されるオブジェクトの外部キーは、呼び出し側モデルの主キーに設定されます。
@author.books << @book1

(↑のcollection部分には、has_manyの引数として設定しているモデルが書ける)

<<を使うと中間モデルにidを追加、保存することができる。ただし、引数で渡すオブジェクトは、インスタンスモデルとして渡す。

  def メソッド名(オブジェクト)
    中間モデル << オブジェクト
  end

https://railsguides.jp/association_basics.html#has-manyで追加されるメソッド-collection-object
https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#singleton_class

実装する

①ビューで行ったボタンを作成

 <%= button_to '行った', restaurant_visits_path(restaurant.id), method: :delete, class: "btn btn-sm btn-secondary" %>
※引数の.idは、書いても書かなくてもコントローラーで取得するparamsの書き方に関係ないけど、分かりやすいので書いた。

link_toだとhttpメソッドがGetになるので、button_toにした。

②モデルにコントローラーのcreateアクションで使う用のメソッドを書く

  has_many :visits, dependent: :destroy
  has_many :visited_rst, through: :visits, source: :restaurant
 
~ 省略 ~

  def visit(restaurant)
    visited_rst << restaurant
  end

注意: visited_rst << restaurantとして追加できるものは、Restaurantのインスタンス

使うときは、current_user.メソッド名(インスタンスモデル)となる。
current_user.visited_rstで、user_idに紐づいたrestaurant_idを取ってこれる。
そのrestaurant_idカラムにrestaurantを収納できる。

③コントローラー

最初に、ビューのparamsを使って、Restaurantモデルのインスタンスを取得する。
次に、そのインスタンスモデルを引数としてモデルに書いたメソッドに渡す。

class VisitsController < ApplicationController
  def create
    @restaurants = Restaurant.find(params[:restaurant_id])
    current_user.visit(@restaurants)
    redirect_to request.referer
  end
paramsの中身は、#<ActionController::Parameters {"authenticity_token"=>"CLDyfA_3HEwZr-32DbARQ2IRH7n2GSq--7Xm1bChbxUGhgYema-lUAqAMco92RrrvLaoNodKGUg2GTLR0EcFMw", "controller"=>"visits", "action"=>"create", "restaurant_id"=>"17"} permitted: false>になっているので、paramsでとりたいものをrestaurant_idに指定する

演算子 "<<"を使わないパターン

①モデル

~ 省略 ~

  def visit(restaurant)
    visits.build(restaurant_id: restaurant)
  end

コントローラーからは、restaurant_id(キー)の値(バリュー)が渡されている。

②コントローラー

class VisitsController < ApplicationController
  def create
    current_user.visit(params[:restaurant_id]).save
    redirect_to request.referer
  end
end

おわり

試行錯誤してて、なんで、引数の値は"string型の数字"が入っているのにエラーにならないのか不思議だったけど、create(buld)メソッドは、キーとバリューのセットで保存されるからかな?となった
https://pikawaka.com/rails/params

visited_rst << restaurantの場合は、current_user_idがあるところに、restaurant_idを追加した感じで、
visits.build(restaurant_id: restaurant)の場合は、同時に作る感じかしら。

Discussion