7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

はじめに

皆さんは、ActiveAdmin使ってますか?

ActiveAdmin、サッと管理画面作るには素晴らしい反面、
ちょっとでも基本的な実装から外れると、途端につまる、、
そんな経験をされた方も多いかと思います。

そして往々として、業務では基本的な実装から外れるものですよね。

そんな格闘の中で得た、「ActiveAdminでちょっと凝ったことをするのに使える(かもしれない)方法」を、
「ActiveAdmin 力技集」 としてシェアしようと思います。

なお、この方法が正しいというわけではなく、こう乗り切ったよ、くらいの感覚で見ていただけるとありがたいです。
もっといい方法があれば、ぜひコメントをお願いします!

環境

Ruby 2.5.5
Rails 5.2.2.1
activeadmin 1.3.1

ActiveAdmin 力技集

Model

顧客がいて、接点があって、請求と明細がある

app/models/customer.rb
class Customer < ApplicationRecord
  has_many :contacts
  has_many :invoices
end
app/models/contact.rb
class Contact < ApplicationRecord
  belongs_to :customer
end
app/models/invoice.rb
class Invoice < ApplicationRecord
  belongs_to :customer
  has_many :items, class_name: 'InvoiceItem'
end
app/models/invoice_item.rb
class InvoiceItem < ApplicationRecord
  belongs_to :invoice
end

絞り込まれた一覧へのリンク

例えば顧客の詳細画面に、その顧客との接点一覧へのボタンを置きたい時。

app/admin/customers.rb
button_to '接点履歴一覧へ', admin_customer_contacts_path, method: :get, params: { q: { customer_id_equals: customer.id } }

検索条件がparams[:q]に入るのがキモ。
filterで検索を実装して実際に検索してみて、そのパラメータを再現すると手っ取り早い。

なお、このパラメータの使用に際して、filterで実装されている必要はない。

別のカラムでソート

app/admin/customers.rb
ActiveAdmin.register Customer do
  index do
    column '顧客ID', :id
    # sortable: にソートしたいカラム名を記述
    column '顧客名', :name, sortable: :kana
  end
end

このように漢字を表示しつつ読み仮名でのソートができる。

formでjsを使う

例えばformで顧客を選択したいがselectだと厳しいので、オートコンプリートを実装したい時などに。

app/admin/contacts.rb
ActiveAdmin.register Contact do
  # ...
  form partial: 'form'
end
app/views/admin/contacts/_form.html.erb
<script language="JavaScript">
  // @contactが暗黙的に渡ってくる。
  var contact = <%= raw @contact.to_json %>
  // ...
</script>

当然、この例では@contactの中身がブラウザで丸見えになるので、セキュリティ等問題がないユースケースか、検証が必要。

関連先によるソート・絞り込み

例えば顧客との接点の一覧画面で、顧客によってソート・絞り込みをしたい時。

app/admin/contacts.rb
ActiveAdmin.register Contact do
  filter :date, label: '年月日'
  # 関連先の型までは見てくれないようなので、as: :string などの指定が必要
  filter :customer_name, as: :string, label: '顧客名'

  index do
    column '年月日', :date
    column '顧客ID', :customer_id
    # sortableには発行されるSQLで使えるカラム名を渡す。今回はcustomersテーブルをjoinしているので、customers.kana
    # なお、関連名を渡すといい感じにリンク化してくれる
    column '顧客名', :customer, sortable: :'customers.kana'
    column '内容', :memo
  end

  controller do
    def scoped_collection
      # 関連先でソートを行う場合はjoinが必要
      end_of_association_chain.joins(:customer)
    end
  end
end

indexの表はscoped_collectionで発行されたSQLの結果を使っている、と考えるとイメージしやすい。

デフォルト検索条件

必ず「ある年月の一覧」を表示したい場合はこのように。

app/admin/invoices.rb
ActiveAdmin.register Invoice do
  # ...
  controller do
    # 必ず年月による絞り込みがかかるよう、indexページ表示前にparamsの不足を補う
    before_action only: :index do
      # 最初に表示されるのは先月分。
      prev_month = Time.current.prev_month
      if params[:q].present?
        params[:q].merge!(year_eq: prev_month.year, month_eq: prev_month.month) if params[:q][:year_eq].blank? || params[:q][:month_eq].blank?
      else
        params.merge!(q: { year_eq: prev_month.year, month_eq: prev_month.month })
      end
    end
  end
end

他の検索条件を邪魔しないように注意が必要。

GROUP BYした結果の一覧を作る

例えば版管理を行う請求書の一覧などで、版違いは同じ行でまとめたい時。

app/admin/invoices.rb
ActiveAdmin.register Invoice do
  index do
    # ...
    column 'バージョン', class: 'minWidth100' do |latest_invoice|
      Invoice
        .where(customer_id: latest_invoice.customer)
        .where(year: latest_invoice.year)
        .where(month: latest_invoice.month)
        .each do |invoice|
        div { span link_to "第#{invoice.version}版", admin_invoice_path(invoice) }
      end
      nil # nilを返さないと、上の行の結果が出力されてしまう
    end
  end

  controller do
    # ...
    def scoped_collection
      # 一覧には顧客ごとの請求書を出したいのだが、返るレコード数分、表の行が作られてしまう。
      # そのため、orderした上でgroup、顧客ごとに最新の請求書だけを取得
      # 参考: https://qiita.com/ryo-ishii/items/36b878cf2d0bd8ef7e07
      Invoice
        .group(:customer_id, :year, :month)
        .from(
          end_of_association_chain
            .select('invoices.*, customers.name, customers.kana, CONCAT(year, LPAD(month, 2, 0)) AS ym')
            .joins(:customer)
            .order(version: :desc),
          :invoices
        )
    end
  end
end

まとめ

ActiveAdminの良いところでもあり辛いところは、暗黙的に定義されている変数が多いところだと思います。
それらをいかに把握して差し込んでいくかが、ActiveAdminで力技をする際に重要になります。

それ以前に、力技を使わなくても済むような要件、データモデルの定義を心がけていきたいですね。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?