2011年12月12日月曜日

「苗字」カラムと「名前」カラムを「フルネーム」に集約する

実行環境:
ruby 1.9.3
Rails 3.1.3
composed_of宣言を使うとテーブルの2つのカラムを疑似的に1つのカラムにまとめることができます。
DBのテーブル上は、「苗字」カラムと「名前」カラムが別になっているが、Rails上はフルネームで扱いたい、なんて場合があります。あるいは住所情報が「都道府県」、「市区町村」、「それ以降の住所」と3つに分かれてテーブルに保存されているのを「住所」という1カラムとして扱いたい、なんて場合もあります。

集約によるデータの合成

例としてこんなテーブルがあったとします。
usersテーブル
カラム1id
カラム2myoji
カラム3namae
カラム4myoji_yomi
カラム5namae_yomi

【目標】:「myoji」と「namae」カラムをスペースでつないだ文字列が「fullname」で参照できるようにする

集約のためのFullNameクラスを定義する

集約のためのルールを定めるクラスを作成します。とりあえずmodelの定義ファイルの中で定義します。
#{RAILS_ROOT}/app/models/users.rb
class User < ActiveRecord::Base end class FullName attr_reader :family_name, :given_name def initialize(family_name, given_name) @family_name = family_name @given_name = given_name end def to_s [@family_name, @given_name].compact.join(" ") end end

テーブル上のカラムとの対応付けを行う

composed_of宣言を使ってfullnameという名前のプロパティ(?)を定義します。元々のテーブルのどのカラムが指定したFullNameクラスのどの変数に対応するかをマッピングします。
#{RAILS_ROOT}/app/models/users.rb
class User < ActiveRecord::Base composed_of :fullname, :class_name => "FullName", :mapping => [ [ :myoji, :family_name ], [ :namae, :given_name ] ] end class FullName # 省略 end

こうすることで、user.fullname という形で参照することができます。
#{RAILS_ROOT}/app/views/users/show.html.erb
<%= @user.fullname %>
FullNameクラスのインスタンス変数(family_name, given_name)にアクセサメソッドを定義しているので以下のような参照も可能です。
#{RAILS_ROOT}/app/views/users/show.html.erb
<%= @user.fullname.first_name %> <%= @user.fullname.given_name %>
もともとのカラムにもアクセスできます。
#{RAILS_ROOT}/app/views/users/show.html.erb
<%= @user.myoji %> <%= @user.namae %>

集約したfullnameに値を代入することはなんだかできないようです。値を変更する場合は、もともとのカラムの値を変えるようにすればいいのかな…?

集約に使うクラスを使い回す

定義したFullNameクラスを「myoji」と「namae」カラムの集約に使いましたが、「myoji_yomi」と「namae_yomi」カラムの集約にも使えます。
#{RAILS_ROOT}/app/models/users.rb
class User < ActiveRecord::Base composed_of :fullname, :class_name => "FullName", :mapping => [ [ :myoji, :family_name ], [ :namae, :given_name ] ] composed_of :fullkananame, :class_name => "FullName", :mapping => [ [ :myoji_yomi, :family_name ], [ :namae_yomi, :given_name ] ] end class FullName # 省略 end
参照はこんな感じで。
#{RAILS_ROOT}/app/views/users/show.html.erb
<%= @user.fullname %> <%= @user.fullname.first_name %> <%= @user.fullname.given_name %> <%= @user.fullkananame %> <%= @user.fullkananame.first_name %> <%= @user.fullkananame.given_name %>
複数のモデルでFullNameクラスを使い回すこともできそうです。そうなってくるとFullNameクラスの定義をする場所はもっといい場所があるかもしれません。(例えば#{RAILS_ROOT}/config/initializers/の中とか??)

結合した値の参照だけならメソッドでいいんじゃない? composed_of の使い方を書いてきましたが、単に fullname を参照するだけならモデルの中にメソッドを定義すればよいような…
#{RAILS_ROOT}/app/models/users.rb
class User < ActiveRecord::Base def fullname "%s %s"%([self.myoji, self.namae]) end end

0 件のコメント:

コメントを投稿