LoginSignup
0
0

More than 3 years have passed since last update.

collection_check_boxes を用いた中間テーブルのコールバック

Last updated at Posted at 2021-03-29

collection_check_boxes を用いて中間テーブルを削除した際に、中間モデルの after_destroy のコールバックを実行したかったが上手くいきませんでした。

モデル

class Route < ApplicationRecord
  belongs_to :user
  has_many :route_spots, dependent: :destroy
  has_many :spots, through: :route_spots
end

class Spot < ApplicationRecord
  belongs_to :user
  has_many :route_spots, dependent: :destroy
  has_many :routes, through: :route_spots
end

class RouteSpot < ApplicationRecord
  belongs_to :route
  belongs_to :spot
  after_destroy :align_sequence

  private
    def align_sequence
      self.route.route_spots.order(sequence: "ASC").each_with_index{ |object, index|
        object.sequence = index + 1
        object.save
      }
    end
end

コントローラ

routes_controller.rb
class RoutesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_id, only: %i[edit destroy update show]

  ...省略

  def edit
    @my_spots = current_user.spots
  end

  def update
    if @route.update(route_params)
      redirect_to routes_path(anchor: "id_#{@route.id}"), notice: "「#{@route.name}」を編集しました"
    else
      render :edit
    end
  end

  ...省略

  private
  def route_params
    params.require(:route).permit(:id, :name, :memo, :sequence, spot_ids:[])
  end

  def set_id
    @route = Route.find(params[:id])
  end
end

view

views/routes/edit.html.slim
= form_with model: route, local:true, id:"route_form" do |f|
  = f.label :name
  = f.text_field :name, id: 'route_name'
  = f.label :memo
  = f.text_area :memo, id: 'route_memo'
  = f.collection_check_boxes(:spot_ids, @my_spots, :id, :name)
  = f.submit '登録する' , class:'button-primary'

やりたかったこと

動作自体は、Route の edit 時に、has_may な Spots の登録/登録解除を、collection_check_boxes で簡単に実装しただけのなんてことのないもの。
やりたかったのは、collection_check_boxes でチェックを外して route を update (routeのupdate と同時に route.route_spots が destroy)した際に、RouteSpotモデルの after_destroy で メソッドalign_sequence を実行したかった。

しかし、after_destroy、before_destroy、どちらでも動作せず、ただ RouteSpot の destroy が完了するだけでした。

結論

あまり良いとは言えないかもしれないですが、RouteSpot のコールバック(after_destroy)ではなく、Route のコールバック(after_update)ので処理することにしました。
もともと、route.route_spots に対し一括で連番を振り直すというものだったので、ひとまずやりたいことは問題なく動作しますので、今回はこれで妥協します。

仕様と言われたら納得いきますが、バグ、しかも長期にわたって放置されているものとだと思うとちょっと微妙な気持ちです。

0
0
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
0
0