Posted at

Rails Ransack sort_linkが使えない統計カラムなどをソートするヘルパーを作ってみた

More than 1 year has passed since last update.


事実確認とやりたいこと


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パラメーターを使ってソートします。


controller

@q = Sales.select('name, sum(price) as total_price').group(:name).order(params[:sort]).ransack(search_params)

@sales = @q.result


View

テーブルのヘッダーに今回作ったヘルパーtable_sort()を指定します。

検索ありです。

※slimを使っています。


view

= 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を返します。


helper

# テーブルのソートリンクを作成する

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パラメータを全て連ねる方法もありますがスマートじゃないと思いやめました。


js

$(function() {

$(document).on("click", ".sort-link", function() {
$('#sort').val($(this).data('colmun') + ' ' + $(this).data('order'));
$('.search-form').submit();
});
});


最後に

ベストプラクティスなのかはわかりませんが、今のところ自分では満足しています。

イイね!と思ったら使ってみてください。