Mainichi Engineering

H29年の新卒Railsエンジニアが日々学んだことを徒然に書きます。

modelに存在しないカラムを仮想的に存在させた状態にする

Railsで登録フォームなどを作っている時に「ここの入力フォームを2つに分割して書かせるようにしたい」という場面がありました。
僕みたいな初心者railsエンジニアは、「DBの設計から変更しなきゃ….」と考えてしまうのですが、先輩に聞くと vartial attributes という方法を使えば簡単に実装できるというので、実際に使ってみました。

vartual attributes(仮想属性)とは

vartual attributesとは、読んで字のごとく仮想的な属性のことです。
これを用いることで、上に書いたようにデータベースを変更しないで、登録・編集フォームを変更することができます。

今回の使用状況

郵便番号の登録フォームを上3桁、下4桁の分割したフォームに変更させる。
例)123-4567の様に"-“をフォームに追加する。

手順

1. 郵便番号を上3桁、下3桁の属性に分ける

# /models/address.rb

#table: adresses
#column
#string: address

class Address < ApplicationRecord
  attr_writer :first_address, :last_address

  validates :address, format: { with: /\A\d{7}\z/ }
  validates :first_address, presence: true
  validates :last_address, presence: true

  #先にset_addressを呼ばないとバリデーションエラーになる
  before_validation :set_address

  def first_address
    @first_address || self.address[0..2] if self.address.present?
  end

  def last_address
    @last_address || self.address[3..6] if self.address.present?
  end

  #DBカラムはaddressのため、バリデーション前に結合して設定する
  def set_address
    self.address = [@first_address, @last_address].join
  end
end

ここでfirst_addressとlast_addressというVirtual Attributes(仮想属性)を追加しています。

注目箇所は、attr_writerというRubyモジュールです。
attr_writerメソッドは、クラスやモジュールにインスタンス変数を書き込むためのアクセサメソッドを定義します。 attr_writerメソッドを使うことで、書き込み用のaddressカラムを分けることができているということになります。

やってないけど、もしフォームで作成したaddressに"-“表示するためには、attr_accesorメソッドを使えばいいってことかな??  

あと

  def first_address
    @first_address || self.address[0..2] if self.address.present?
  end

この文では、住所に入る数値の制限も書かれているのがわかります。
これで、first_address、last_addressという、属性が追加されました。

2. view画面に郵便番号入力欄を追加する

次に郵便番号入力欄を2つに分けて入力できるようにします。

#views/addresses/_form.html.haml

= form_for @address do |f|
  - if @address.errors.any?
    #error_explanation
      %h2= "#{pluralize(@address.errors.count, "error")}"
      %ul
        - @address.errors.full_messages.each do |msg|
          %li= msg

  .field
    = f.label :address
    = f.text_field :first_address
    = f.text_field :last_address

  .actions
    = f.submit 'Save'

二つに分けた"“f.text_field”“を追加で書きます。

3. StrongPararametersにfirst_addressとlast_addressの入力を許可させる

最後にcontrollerのStrongPararametersにfirst_addressとlast_addressの入力を許可させます。

#controllers/addresses_controller.rb

private
    def address_params
      params.require(:address).permit(:first_address,
                                      :last_address)
    end
end

フォーム画面 f:id:mainichiaisatu:20170326225130p:plain

これで追加できるようになったかと。
とりあえずひと安心。。。