Ruby on RailsのActive Recordではコールバックを利用することで、データベースのレコード操作を行う前後に任意の処理を追加することができます。
これによりデータに対する前処理・後処理などを行うことができるので、柔軟なデータハンドリングが実現できます。
また、同様にアクションの前後に対してフック処理を挿入する機能もあります。

基本的なコールバック・フックの追加方法や、用意されている各種コールバック(フック)ポイントを把握しておきましょう。

オンラインでプログラミング学習を始めてみたいなら、米国シリコンバレー発祥の世界最大級のオンライン学習プラットフォーム、Udemyがおすすめです。
こちらの記事で紹介するフレームワーク、Ruby on Railsも学ぶことができます!
よくわかるRuby on Rails入門 オンライン講座

コールバック・フックについて

コールバックとは、Active Recordでデータベースのレコードに対して操作を行う際に、あらかじめ指定されている様々なタイミングで処理を呼び出す機能のことです。
ある特定の処理にひっかけて(紐づけて)別の処理を呼び出すイメージです。

同様にして、任意のアクションが呼び出される前後に処理を挿入する機能をフック、あるいはフィルターと呼びます。

これら2つは非常に似ており、データに前処理・後処理を施したり、ユーザのログイン状態に応じて遷移させる画面を分岐するなどの目的で利用されます。

共通化された処理をまとめることができる、DRY(Don’t Repeat Yourself = 同じコードを繰り返し書かない)の理念を遵守した機能となっています。

今回使用するサンプルアプリケーションは以下の手順で再現できます。
同じ環境で試してみたい方は参考にしてみてください。

# 新規プロジェクトの作成
$ rails new sample-app
$ cd sample-app

# scaffoldコマンドでUserモデルの関連ファイルを自動生成
$ rails generate scaffold user name:string email:string

# データベースの作成
$ rails db:create

# マイグレーションの実行
$ rails db:migrate

現場を意識した本格的な学習カリキュラムで、本物のスキルを身につけたいならこちら!
困ったときにいつでも相談できるバディ制度でわからないこともすぐに解決できます。
プログラミングスクールといえば【RUNTEQ】

Active Recordのコールバック

実際にコールバックを登録してみます。
以下はよくあるケースで、レコードを保存する前にメールアドレスをすべて小文字にする、という処理を登録しています。
Active Recordを使用して新しいレコードを保存する際に自動で実行されます。

class User < ApplicationRecord
  before_save do
    self.email = email.downcase
  end
end

また、以下のようにメソッドとして記述しておいて呼び出す方法もよく使用します。

class User < ApplicationRecord
  before_save :downcase_email

  private
    def downcase_email
      self.email = email.downcase
    end
  end
end

以上で処理が登録できましたので、実際にデータを登録して動作を試してみましょう。

app/models/user.rb

レコードがデータベースに登録される段階でコールバック処理が実行され、emailがすべて小文字になっているのがわかります。

オプションによるアクションの限定

onオプションを使用することで、指定したアクションのみでコールバックを呼び出すことができます。

class User < ApplicationRecord
  before_save :downcase_email, on: :create

  private
    def downcase_email
      self.email = email.downcase
    end
  end
end

複数のアクションを指定したい場合は以下のように指定します。

class User < ApplicationRecord
  before_save :downcase_email, on: [:create, :update]

  private
    def downcase_email
      self.email = email.downcase
    end
  end
end

コールバックポイント一覧

ここまではコールバックの処理を行うタイミングをbefore_saveにしていましたが、それ以外のタイミングも指定することができます。
利用可能なコールバックポイントとその実行タイミングは以下の通りです。

コールバックポイント概要
after_findfind、find_by、first、lastメソッドでオブジェクトが見つかったときに実行(検索結果の数分コールバックが実行)
after_initializeオブジェクトのインスタンスが生成されたときに実行
before_validationバリデーションが実行される直前に実行
after_validationバリデーションが実行された直後に実行
before_saveオブジェクトがDBに保存(create、update)される直前に実行
around_saveオブジェクトがDBに保存(create、update)される前後に実行
after_saveオブジェクトがDBに保存(create、update)される直後に実行
before_createオブジェクトがDBに追加(create)される直前に実行
around_createオブジェクトがDBに追加(create)される前後に実行
after_createオブジェクトがDBに追加(create)される直後に実行
before_updateオブジェクトがDBに更新(update)される直前に実行
around_updateオブジェクトがDBに更新(update)される前後に実行
after_updateオブジェクトがDBに更新(update)される直後に実行
before_destroyオブジェクトがDBから削除(destroy)される直前に実行
around_destroyオブジェクトがDBから削除(destroy)される前後に実行
after_destroyオブジェクトがDBから削除(destroy)される直後に実行
after_commitオブジェクトがDBにコミットされた直後に実行
after_rollbackオブジェクトがDBへの保存に失敗したときに実行、またはバリデーションエラー時に実行
after_touchtouchメソッドが呼び出されたときに実行
touch:updated_atを現在時刻で更新するメソッド

コールバックのクラス化

Railsでは、Controllerへの記述をなるべく減らし、Modelに処理をまとめることが多いです。
そうなると、Modelへの記述量が増えすぎた場合にいわゆるファットモデルという状態に陥ります。

これを防ぐためにも、コールバックをクラス化する方法を知っておくと便利です。

また、コールバックをクラス化しておくことで、複数のモデルでの使いまわしも可能になります。

class Callbacks
  def before_save(email)
    email = email.downcase
  end
end
class User < ApplicationRecord
  before_save Callbacks.new 
end
$ rails console

irb(main):001:0> user = User.new
   (1.3ms)  SELECT sqlite_version(*)
=> #<User:0x000002786cc8fde8 id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>

irb(main):002:0> user.name = 'Ichigo'
=> "Ichigo"

irb(main):003:0> user.email = 'ICHIGO@hash-drip.com'
=> "ICHIGO@hash-drip.com"

irb(main):004:0> user.save
  TRANSACTION (0.1ms)  begin transaction
  User Create (1.6ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Ichigo"], ["email", "ichigo@hash-drip.com"], ["created_at", "2021-11-08 03:24:03.682872"], ["updated_at", "2021-11-08 03:24:03.682872"]]
  TRANSACTION (3.3ms)  commit transaction
=> true

irb(main):005:0> user.email
=> "ichigo@hash-drip.com"

プログラミングでわからないことがあったら、その道のプロに質問するのが一番!
現役で活躍中のエンジニアと繋がって、効率的にWeb開発のスキルを身につけましょう。
プログラミング学習のスキルプラットフォーム【MENTA】

アクションのフック(フィルター)処理

フック(フィルター)はコールバックと同様の方法でコントローラ内に設定することができます。
アクションが実行される直前や直後に任意の処理を挿入できます。

class ApplicationController < ActionController::Base
  before_action :require_login

  private
    def require_login
      unless logged_in?
        redirect_to login_path
    end
  end
end

フックポイント一覧

利用可能なフックポイントとその実行タイミングは以下の通りです。

フックポイント概要
before_actionアクションが実行される直前に実行
after_actionアクションが実行された直後に実行
around_actionアクションが実行される前後に実行

まとめ

コールバックやフックを使うことで、データの前処理・後処理を行ったり、ページが呼び出される前後に任意の処理を挿入したりできます。
これらの機能を活用することで実装の漏れも減り、アプリケーションが予想外の挙動をすることを防げます。

ひとりではなかなかプログラミングの学習が続かない、未経験だから不安が多い、という方はプログラミングスクールを利用してみるのも有効です。
本物のエンジニアに学ぶことで、時間がない方でも最短でスキルを身につけることができます。
現役エンジニアのパーソナルメンターからマンツーマンで学べる

お悩みの方は、まずは無料キャリアカウンセリングにお申込みください。

関連記事

この記事のタグ