Administrateで配列型のフィールドを扱う

Categories
Tags
Share

Administrateは何度か紹介していますが、管理画面を作るのに便利なgemです。

以前の記事:gem administrateを使ってみた

今回は、そのAdministrateでPostgrSQLの配列型を扱うためのCustom Field TypeであるArrayTypeFieldを作ったので、それを紹介します。

Custom Field Typeとは?

Administrateでは、DBのカラムに合うように、デフォルトで様々なフィールド用の型が定義されています。 それをを使えば簡単に入力フォームが作れるのですが、複雑な条件になるようなカラムだと用途に合いません。

しかし、そういう場合はユーザーが自分用にフィールド用の型を定義することができます。それが、Custom Field Typeです。

今回定義したCustom Field Typeは、PostgreSQLの配列型に対応するためのCustom Field Typeです。

ArrayTypeFieldを定義する

では、ArrayTypeFieldを定義していきます。

自動生成

Administrateには、Custom Field Typeの雛形を作るためのジェネレータが定義されていますので、それを使います。

rails g administrate:field ArrayType

すると、いくつかファイルが作られます。

  • app/fields/array_type_field.rb
  • app/views/fields/array_type_field/_show.html.erb
  • app/views/fields/array_type_field/_index.html.erb
  • app/views/fields/array_type_field/_form.html.erb

ArrayTypeFieldクラスの修正

デフォルトだと、to_sメソッドしかないので、もろもろ定義していきました。 choicesメソッドでは、Dashboardで渡されるオプションに応じて、選択項目用の値を整形しています。 Hashならばそのまま返しています。

require "administrate/field/base"

class ArrayTypeField < Administrate::Field::Base

  def to_s
    choices = options.fetch(:choices)
    if choices.is_a? Hash
      hash = choices.invert
      data.map { |id| hash[id] }.join(', ')
    else
      data.join(', ')
    end
  end

  def choices
    choices = options.fetch(:choices)
    if choices.is_a? Hash
      choices
    else
      choices.zip(choices)
    end
  end

end

フォームの修正

  • app/views/fields/array_type_field/_form.html.erb

このファイルを修正します。配列型のフィールドなので、複数選択可能なCheckboxにしています。 敢えて、check_box_tagメソッドを使っています。というか使わないと複数選択可能なチェックボックスが作れません。

<div class="field-unit__label">
  <%= f.label field.attribute %>
</div>
<div class="field-unit__field">
  <% field.choices.each.with_index(1) do |(key, value), idx| %>
    <label>
      <%= check_box_tag "#{f.object_name}[#{field.attribute}][]", value, field.data.include?(value), id: "#{field.attribute}_#{idx}" %>
      <%= key %>
    </label>
  <% end %>
</div>

これで、フォームは出来上がりました。

Dashboardの修正

では、Dashboardの修正を行います。今回は、Shopモデルのダッシュボードとします。 このShopモデルにpref_idsという出店先の都道府県を保存する配列型のカラムがあるとします。 これを以下のようにwith_optionsのchoiceにHashを渡します。

require "administrate/base_dashboard"

class ShopDashboard < Administrate::BaseDashboard
  ATTRIBUTE_TYPES = {
    # 他の項目は省略…
    pref_ids: ArrayTypeField.with_options(choices: {"北海道": 1, "青森県": 2, "岩手県": 3, …})
  }.freeze
end

このように設定した状態で、入力フォームを見てみましょう!

ArrayTypeFieldを使ったフォーム

いい感じですね。

Strong Parametersの設定

そして、忘れてはならないのが、Strong Parametersの設定です。

  • app/controllers/admin/shops_controller.rb

このファイルがあるとして、このファイルを編集します。

module Admin
  class ShopsController < Admin::ApplicationController
    def update
      requested_resource.assign_attributes(resource_params)
      requested_resource.pref_ids = [] if resource_params[:pref_ids].nil?
      if requested_resource.save
        redirect_to(
            [namespace, requested_resource],
            notice: translate_with_resource("update.success"),
        )
      else
        render :edit, locals: {
            page: Administrate::Page::Form.new(dashboard, requested_resource),
        }
      end
    end

    private

    def permitted_attributes
      dashboard.permitted_attributes << [{ pref_ids: [] }]
    end
  end
end

permitted_attributesメソッドをオーバーライドしておきます。 また、updateメソッドも変更しています。これは、チェックボックスを全て外した場合に値が更新されないのを防ぐための措置を入れています。

これで、配列型を扱うことができるようになりました!

保存後の画面

フォームにて、チェックを入れて保存してみたら、以下のようにカンマ区切りで表示されるようになりました。

ArrayTypeFieldを使って保存したデータの表示

これは、ArrayTypeFieldクラスのto_sメソッドでこのように表示するようにしています。

def to_s
  choices = options.fetch(:choices)
  if choices.is_a? Hash
    hash = choices.invert
    data.map { |id| hash[id] }.join(', ')
  else
    data.join(', ')
  end
end

カンマ区切りではダメとかであれば、ここを編集して見た目を調整しましょう。

まとめ

Administrateだと配列型でも怖くありません。 多少面倒くさいところはありますが、管理画面でデフォルトで扱える型に縛られてしまうほうが、 DBのパフォーマンスを発揮できなかったり、後々の実装で困ったりすると思うので、 自由度の高いライブラリを使うほうがいいかなと思います。


comments powered by Disqus