Railsのコールバックまとめ


こんにちは、鈴木です。

Techscore 本体の記事の下書きを書き進めています。

ということで、コールバックの記事の下書きを公開します。

 

コールバックの種類

コールバックとは、バリデーションの実行やデータベースへの保存などのタイミングで処理を行うための機能です。

あるタイミングで必ず実行する必要がある処理をコールバックに指定することで、モデルの一貫性を保つことができます。

Rails のコールバックの種類をまとめてみると、非常に多いことが分かります。

 

after_find

検索メソッドでオブジェクトが見つかったタイミングで実行されます。

検索条件を指定せずに all メソッドを呼び出すなど、検索結果が大量になる場合は注意が必要です。

検索結果の数だけコールバックが実行されますので、パフォーマンスに重大な影響を及ぼす可能性があります。

 

after_initialize

オブジェクトがインスタンス化されたタイミングで実行されます。

User.new のようにインスタンス化した場合は、after_find は実行されずに after_initialize のみが実行されます。

after_find と同様、検索条件を指定せずに all メソッドを呼び出すなど、検索結果が大量になる場合は注意が必要です。

インスタンス化された分だけコールバックが実行されますので、パフォーマンスに重大な影響を及ぼす可能性があります。

 

before_validation

バリデーションが行われる直前で実行されます。

主にバリデーション前に属性値を微調整する場合に用いられます。

このタイミングでは属性値にどのような型のどのような値が設定されているか保証は無いことに注意しましょう。

データベース上では文字列型であっても、数値が代入されているかもしれませんし、nil であるかもしれませんので、想定外のエラーが発生しないように気を付けましょう。

 

after_validation

バリデーションが行われた直後に実行されます。

バリデーションに成功した後ではなく、単純にバリデーションが行なわれた後に実行されます。

そのため、before_validation と同様に、属性値にどのような型の値が設定されているか分かりませんし、nil であるかもしれません。

 

before_save

バリデーションに成功し、実際にオブジェクトが保存される直前で実行されます。

INSERT される場合も、UPDATE される場合も呼び出されます。

INSERT もしくは UPDATE の場合だけ実行したい処理があるときは、後述する before_create / before_update を使用します。

 

before_create / before_update

before_save の後に実行されます。

オブジェクトが登録されるとき (new_record? が true のとき) は before_create が実行されます。

オブジェクトが更新されるとき (new_record? が false のとき) は before_update が実行されます。

登録と更新のどちらの場合にも同じ処理を行うのであれば、before_save を使用すると便利です。

 

after_create / after_update

オブジェクトが保存された直後 (after_save の直前) に実行されます。

オブジェクトが登録されたときは after_create、更新されたときは after_update が実行されます。

登録と更新のどちらの場合にも同じ処理を行うのであれば、after_save を使用すると便利です。

 

after_save

after_create / after_update の直後、データベースへの COMMIT の直前に実行されます。

保存されたオブジェクトの関連オブジェクトを操作するなど、データベースで言うトリガーのような処理を行なう場合に使用します。

 

after_commit

after_save の後(データベースに COMMIT された後)に実行されます。

 

after_rollback

バリデーションエラーや SQL 実行時にエラーが発生した場合に実行されます。

 

after_touch

touch メソッドが呼び出された直後に実行されます。

この場合は before_save や after_save など他のコールバックは実行されません。

 

before_destroy

destroy メソッドでオブジェクトが削除される直前に実行されます。

削除の場合は before_destroy → after_destroy → after_commit の順番で実行されます。

注意として delete/delete_all メソッドで削除した場合はコールバックが呼び出されません。

 

after_destroy

オブジェクトが削除された直後に実行されます。

データベース外で管理しているリソースを削除する場合などに使用することが多いでしょう。

 

コールバックが実行されるタイミング

コールバックは常に実行されるわけではありません。

オブジェクトを更新するメソッドであっても、コールバックが常に実行されないものもあります。

以下のメソッドはコールバックを実行します。

  • create
  • create!
  • decrement!
  • destroy
  • destroy_all
  • increment!
  • save
  • save!
  • save(:validate => false)
  • toggle!
  • update
  • update_attribute
  • update_attributes
  • update_attributes!
  • valid?

以下のメソッドは after_find 及び after_initialize を実行します(after_initialize はオブジェクトを new したときにも実行されます)。

  • all
  • first
  • find
  • find_all_by_attribute
  • find_by_attribute
  • find_by_attribute!
  • last

以下のメソッドはコールバックを実行しません。

  • decrement
  • decrement_counter
  • delete
  • delete_all
  • find_by_sql
  • increment
  • increment_counter
  • toggle
  • touch
  • update_column
  • update_all
  • update_counters

コールバックでビジネスロジックに関わる処理を行っている場合などには、コールバックがスキップされるメソッドがあることに注意しましょう。

コールバックの設定

コールバックを設定する方法は何通りかあります。

一つ目は実行したい処理をブロックで指定する方法です。

例えばオブジェクトが保存される前にパスワードをハッシュ化したい場合は以下のように指定します。

二つ目はコールバックしたいメソッド名をシンボルで指定する方法です。

処理内容が多い場合や、他の箇所からも呼び出される可能性がある場合はこの方法が良いでしょう。

三つ目は、コールバック処理をもつクラスを定義し、そのインスタンスを指定する方法です。

この方法は、同じ処理を複数のモデルで共有したい場合に使用すると良いでしょう。

ちなみにですが、コールバックは以下のように複数指定することもできます。

コールバックを複数指定した場合は、指定した順番に実行されます。

例えば、上記の通りにコールバックを設定した状態で User オブジェクトを save すると、「before_save 1」「before_save 2」の順に出力されます。

 

条件付きコールバック

ある条件を満たした場合だけコールバックを実行したい場合は :if オプションを使用します(:unless オプションもあります)。

先ほどの例では hash_password の中でパスワードが指定されているか判定していましたが、ここでは判定処理を password_required? メソッドで行なわれます。

:if オプションにはメソッド名ではなく Proc オブジェクトを指定することもできます。

 

保存処理の中断

before_validation で指定した処理が false を返した場合、オブジェクトの保存処理は中断されます。

最後に実行した処理が意図せず false とならないように注意しましょう。

オブジェクトの保存処理が中断されると、それ以降のコールバックはキャンセルされます。

そして、save メソッドが呼び出されていた場合は、呼び出し元に false が返されます(save が false を返します)。

save! が呼び出されていた場合は例外 ActiveRecord::RecordInvalid が発生します。

 

まとめ

コールバック処理を活用すると、モデルの一貫性を簡単に保つことができます。

一方で、destroy メソッドはコールバックを実行する、delete メソッドはコールバックを実行しない、update_attribute もコールバックを実行しない、・・・という決まりも理解しておくことが重要です。

 


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です