LoginSignup
29
26

More than 5 years have passed since last update.

ActiveRecordのdestroyでバリデーションをやりたい

Posted at

どうも、ばばです。

destroyするときにバリデーションを走らせたい

たとえば、『destroy出来る条件』みたいなのがあって、その条件をパスしないと消せない、的な感じにしたいとする。
あり得るとしたら、『TwitterとGitHubの両方の認証手段がある(Qiitaみたいにね)んだけど、両方消しちゃったらログインする方法が無くなっちゃうから、両方とも消すことは出来ない』みたいな事がしたかったとする。
方法としては、

  1. destroyアクションの中で弾く
  2. バリデーションを書く

の2つの方法がぱっと思いつくと思う。ただ、バリデーションの方はちょっと特殊なことをやらなきゃいけないし、アクションの中で書くのは『えーそれモデルの仕事でしょー』感がハンパない。負けた感ある。

というわけで、今回はバリデーション側でなんとかスマートに解決させることは出来ないか、っていうのをやってみようと思う。

before_destroyを活用する

before_destroyについては、ActiveRecord::Callbacksに詳しく書いてある。

たとえば、こんな使い方をする。

user.rb
class User < ActiveRecord::Base
  before_destroy { |record| Post.destroy_all(user_id: record.id) }
end

いや、dependentオプションでやれよ、っていうのは気にしないで。

また、こいつ、メソッド名を渡すことも可能だ。

user.rb
class User < ActiveRecord::Base
  before_destroy :delete_all_his_posts

  private

  def destroy_all_his_posts
    self.posts.delete_all
  end
end

これを利用してどうにかしてみよう。

たとえば、saveメソッドは、truefalseを返してくる。trueなら保存成功、falseなら保存失敗だ。
しかし、destroyは基本的に自分自身を返してくる。これに『もし何かに引っかかってエラーになったら、falseを返す』ようにしたら、saveと同じような挙動にすることが出来るのではないか。

login_provider.rb
class LoginProvider < ActiveRecord::Base
  belongs_to :user

  before_destroy :user_should_have_at_least_one_login_provider

  private

  def user_should_have_at_least_one_login_provider
    # もしユーザーのLoginProviderが1つしか無いのに、消そうとしていたら
    if self.user.login_providers.length == 1
      errors.add :base, '少なくとも1つ、ログイン用の認証が必要です'
      return false
    end
  end
end

すると、

if @login_provider.destroy
  redirect_to user_login_providers_path, notice: '削除しました'
else
  redirect_to user_login_providers_path, notice: '削除できませんでした'
end

こんなことが出来るようになる。
もちろん、Viewで@login_provider.errorsを表示すれば、ちゃんと『少なくとも1つ、ログイン用の認証が必要です』と表示される。
ちょっと強引だけど、こんなことをしなきゃいけない時点でだいぶレール踏み抜いてる感があるので、まぁ仕方ないかなぁ。

だいたいそんな感じ( ˘ω˘)

29
26
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
29
26