Azuki'sLog

都内で働くエンジニアのブログ!RubyとかPHPとか

Railsでページネーションを自作する

Railsでページネーションをgemを使わないで実装した事について書きます。
「ページネーションを使える様にしましょう」という事は良くあり、、というかWEB開発では必ずある。
今まではRailsでは kaminari とか will_paginate を使ってきて、便利だし個人的には使えば良いと思っている。

今回は特殊な事情から自作する事になった。

何故自作したか

  1. アプリの構成が特殊で、kaminariを使おうとしたけど何故かkaminariが正常にロードされなかった。
  2. 1. の問題を深掘りしたい気持ちもあったものの、作ってしまった方が早いとなった。
  3. カスタマイズしやすいし、アプリの影響範囲もわかるので自作の良い点も多い。

ページネーションの処理をmodelによせたかった

初めはコントローラ側にページネーションの処理を書いていたのですが、
コントローラにこんな感じのコードが複数発生してきて、共通化できる様にしたかった。

@page = params[:page].to_i < 1 ? 1 : params[:page].to_i
@offset = PER_PAGE * (@page - 1)
relation = Huga.where(coditions)
@hugas = relation.offset(@offset).limit(PER_PAGE).order(id: :desc)
@total_count = relation.count

modelに移した

modelに移す上で、kaminariのコード を見てみると、ActiveRecord::QueryMethods#extending というメソッドが使われている。
ActiveRecord::Relation にメソッドを追加したり拡張ができるみたいで便利!
ドキュメントの例もページネーションだし、これ使ってみようという気持ちになる。
という事で実装してみた所、以下の感じになった。簡単・・・。

module Hoge
  module Concerns
    module Paginate
      extend ActiveSupport::Concern

      included do
        scope :page, ->(page, per_page) do
          all.extending(Hoge::Concerns::Paginate::Extends).paginate(page, per_page)
        end
      end

      module Extends
        def paginate(page, per_page)
          @current_page = page.to_i < 1 ? 1 : page.to_i
          offset = per_page * (@current_page - 1)
          offset(offset).limit(per_page)
        end

        def current_page
          @current_page
        end

        def limit_value
          self.values[:limit]
        end

        def offset_value
          self.values[:offset]
        end

        def total_count
          except(:limit, :offset).count
        end
      end
    end
  end
end

使い方

class Huga < ApplicationRecord
  include Hoge::Concerns::Paginate
end

hugas = Huga.page(3, 30) # 1ページ30件の3ページ目を取得する
hugas.current_page # 3
hugas.limit_value # 30
hugas.offset_value # 60
hugas.total_count # 全件の件数