事実確認とやりたいこと
Ransackのsort_linkでは統計カラムのソートができない
Railsプロジェクトにおいて検索機能を「Ransack」というgemを使って作成している場合、
sort_link()というヘルパー一発でソート機能を実装できます。
しかし、それは単にModelに存在しているカラムのソートが簡単に実装できるヘルパーであってSUMなどの統計後のデータに対してのソートはできません。
例えば以下のような売上データがあるとします。
salesテーブル
id | name | price |
---|---|---|
1 | うまい棒 | 10 |
2 | うまい棒 | 10 |
3 | よっちゃんイカ | 30 |
4 | うまい棒 | 10 |
5 | うまい棒 | 10 |
このテーブルをSUMで統計して売上合計が大きい順にソートしたいです。
期待している出力結果は以下の通りです
name | price |
---|---|
うまい棒 | 40 |
よっちゃんイカ | 30 |
しかし、sort_link()
ではModelに存在しているカラムしか指定できません。
sort_link(@q, :price)
では以下のような出力となってしまいます。
name | price |
---|---|
よっちゃんイカ | 30 |
うまい棒 | 40 |
やりたいこと
sort_link(@q, :total_price)
みたいなのができたらベストだと考えます。
ぐぬぬぬ。何かいい方法はないのでしょうか?
ソートするヘルパーを作ってしまえ
「ないのなら作ってしまおう。今後も使うことあると思うし!」
と勢いで作ってみました。
JSまで巻き込んでしまったのは自分でもいい判断だったのかはわからないですが、
こんな感じに実装しました。
Controller
orderでsortパラメーターを使ってソートします。
@q = Sales.select('name, sum(price) as total_price').group(:name).order(params[:sort]).ransack(search_params)
@sales = @q.result
View
テーブルのヘッダーに今回作ったヘルパーtable_sort()を指定します。
検索ありです。
※slimを使っています。
= search_form_for @q, url: sales_path, class: 'search-form' do |f|
= hidden_field_tag :sort
= f.search_field :name_cont,
= f.submit '検索'
table
thead
tr
th = "name"
th = table_sort('price', 'total_price')
- @sales.each do |item|
tr
th = item.name
th = item.total_price
Helper
ヘッダーに表示するテキストとソートする対象の名前を引数に、
href="javascript:void(0)"
のlink_toを返します。
# テーブルのソートリンクを作成する
def table_sort(text, target)
order = (params[:sort].present? && params[:sort].include?(target) && params[:sort].include?('asc')) ? 'desc' : 'asc'
text += params[:sort].present? && params[:sort].include?(target) ? params[:sort].include?('asc') ? ' ▲' : ' ▼' : ''
link_to text, 'javascript:void(0)', class: 'sort-link', data: {colmun: target, order: order}
end
Javascript
今回JSを使用したのは検索条件を引き継ぐためです。
リンクの最後にgetパラメータを全て連ねる方法もありますがスマートじゃないと思いやめました。
$(function() {
$(document).on("click", ".sort-link", function() {
$('#sort').val($(this).data('colmun') + ' ' + $(this).data('order'));
$('.search-form').submit();
});
});
最後に
ベストプラクティスなのかはわかりませんが、今のところ自分では満足しています。
イイね!と思ったら使ってみてください。