delegateを使って関連先データを簡潔に取得する

こんにちは。デザイン・システム室の永野です。

今回は、Rails の delegate を用いた関連先データの取得方法についてまとめてみました。

以前から存在自体は認識していたのですが、あまり使用したことがなかったので、試しに使用してみると、意外に便利なことに気づきました。

これからdelegateを用いて、関連先データを取得してみたいと考えている方の参考になれば幸いです。

delegate とは??

delegate は、関連するモデルやオブジェクトの属性やメソッドを簡潔に呼び出せるようにするためのヘルパーメソッドです。これにより、コードの可読性を向上させ、冗長な記述を減らすことができます。

使い方は下記の通りです。

# 対象のUserモデルのインスタンスにひもづくProfileモデルの nameカラムの値が、「テスト太郎」とする
> User.last.profile.name
=> "テスト太郎"

# user.rb
class User < ApplicationRecord
  has_one :profile

  def name
    profile.name
  end
end

# Userモデルに定義したnameメソッドを実行し、nameカラムの値を取得
> User.last.name 
=> "テスト太郎"

# delegate を使用し、profiles テーブルのnameカラムの値を取得
class User < ApplicationRecord
  has_one :profile

  delegate :name, to: :profile
end

> User.last.name 
=> "テスト太郎"

参考

Active Support コア拡張機能 – Railsガイド

delegateを用いた関連先データの取得

今回は、下記のようなモデルでリレーションが組まれていた時、customer_coupons テーブルから customer_coupons > serial_numbers > items > unifide_codes テーブルの code カラムの値を取得したいと考えていました。

class CustomerCoupon < ApplicationRecord
  belongs_to :serial_number, optional: true
end

class SerialNumber < ApplicationRecord
  has_one :customer_coupon
end

class Item < ApplicationRecord
  belongs_to :unified_code, optional: true
  has_many :serial_numbers
end

class UnifiedCode < ApplicationRecord
  has_one :item, dependent: :destroy
end

そこで、当初は、下記のようなかたちでcode カラムの値を取得しようと考えていました。

# code カラムの値を取得
> CustomerCoupon.last.serial_number.item.unified_code.code

=> "2005181101"

ただ、これだと、customer_coupons テーブルにひもづくunified_codes テーブルの code カラムの値を取得したいとなった時、毎回このような記述をするのは少し冗長な気がしました。

また、customer_coupons テーブルにひもづくunified_codes テーブルの code カラムの値を取得するためだけに、CustomerCouponモデルに unified_codes テーブルの code カラムの値を取得するためのインスタンスメソッドを作成するのも、少し違う気がしました。

そこで、delegate を使用し、より簡単にcodeカラムの値を取得していきたいと思います。

実装した内容が下記の通りです。

class CustomerCoupon < ApplicationRecord
  belongs_to :serial_number, optional: true
  
  delegate :item, to: :serial_number
  delegate :code, to: :item, prefix: true
end

class Item < ApplicationRecord
  belongs_to :unified_code, optional: true
  has_many :serial_numbers
  
  delegate :code, to: :unified_code
end

まず、行なっていることは、CustomerCoupon モデルで、serial_number 経由で、itemを取得し、CustomerCoupon.last.serial_number.item で item を取得できるようにしています。

また、Itemモデルで、unified_code 経由で、code カラムの値を取得しています。

そして、CustomerCoupon モデルで、item 経由で、code カラムの値を取得しています。

また、prefix を用いることで、CustomerCoupon.last.item_code といったかたちで、customer_coupons テーブルから unified_codes テーブルの code カラムの値を直接取得することができます。

CustomerCoupon.last.item_code の実行結果が下記の通りです。

> CustomerCoupon.last.item_code

=> "2005181101"

上記より、delegate を用いて、customer_coupons テーブルから unified_codes テーブルの code カラムの値を取得することができることを確認できました。

まとめ

今回は、関連先のデータをdelegate を用いて取得する方法を紹介しました。

個人的には、とても便利な機能であると思いました。

また、delegate 自体メソッドとして扱われるので、render json: coupon.as_json(methods: [:serial, :item_code]) のように、JSON形式で実行結果をフロント側に返すことも可能らしいです。

今後も delegate を適切なかたちで使用していきたいです。

現在デザイン・システム室では、新しいメンバーを募集しています。
少しでも興味を持たれた方は、ぜひご応募ください。
皆様からのご応募、心よりお待ちしております。