« DBIx::ClassやFormValidator::Simple::Plugin::DBIC::UniqueをつかってTropy | メイン | FormValidatorとDBIx::Class::WebFormの組み合わせはいい。 »

Data::Pageとページナビゲーション

検索などで多くのページがヒットしたときの、ページナビゲーション

<< 3 4 5 6 *7 8 9 10 11 12 >>

の部分をうまく扱って、100ページ分とかあっても、前後のページのみ表示するモジュールを探していたんだけど、良いのが見つからない。
Data::Pagesetというのがあるんだけど、思ったのと違う。

トータルエントリーが30、1ページ5つのエントリーとして、ナビゲーションのリンクは4つとした場合に、Data::Pagesetでは、

ページ1の場合 *1,  2,  3,  4
ページ2の場合  1, *2,  3,  4
ページ3の場合  1,  2, *3,  4
ページ4の場合  1,  2,  3, *4
ページ5の場合 *5,  6
ページ6の場合  5, *6   

となってしまいます。*がついているところは現在のページを表してます。
希望としては、

ページ1の場合 *1,  2,  3,  4
ページ2の場合  1, *2,  3,  4
ページ3の場合  2, *3,  4,  5
ページ4の場合  3, *4,  5,  6
ページ5の場合  3,  4, *5,  6
ページ6の場合  3,  4,  5, *6  

こうなってほしいのです。


Data::Pagesetの場合のソースは↓

foreach my $current_page (1..6){
    my $pageset = Data::Pageset->new({
        total_entries=>$total_entries,
        entries_per_page=>$entries_per_page,
        current_page=>$current_page,
        pages_per_set=>$pages_per_set,
    });
    print join ",", "p$current_page",map {sprintf("% 3s",($_ == $current_page) ?
 "*$_" : $_)} @{$pageset->pages_in_set};
    print "\n";
} 


Data::Page::Pagesetというのもあるけど、ちょっと違う。

んで、思った通りのモジュールが見当たらないので、ちょっとひどい実装だけど自分で組んでみた。

Data::Pageを継承して、pages_in_setメソッドを追加しています。モジュール名も適当。

package DataPageSet;

use strict;
use warnings;
use base qw(Data::Page);

__PACKAGE__->mk_accessors(qw(pages_per_set));

sub new {
  my $class = shift;
  my $self  = {};
  bless($self, $class);

  my ($total_entries, $entries_per_page, $current_page, $pages_per_set) = @_;
  $self->total_entries($total_entries       || 0);
  $self->entries_per_page($entries_per_page || 10);
  $self->current_page($current_page         || 1);
  $self->pages_per_set($pages_per_set || 10);
  return $self;
}

sub pages_in_set(){
    my $self = shift;
    my $pages_per_set = shift;	
	
    $pages_per_set ||= $self->pages_per_set || 10;
    return ($self->first_page..$self->last_page) if $pages_per_set >= $self->last_page;
	
    my $prev = $self->current_page - 1;
    my $next = $self->current_page + 1;
    my @ret = ($self->current_page);
    my $i=0;
    while(@ret < $pages_per_set){
        if($i%2){
            unshift(@ret,$prev) if $self->first_page <= $prev;
            --$prev;
        }else{
            push(@ret,$next) if $self->last_page >= $next;
            $next++;
        }
        $i++;
    }
    return @ret;
}

1;


全くヒドい実装だが、とりあえず動く。
使い方は、Data::Pageと同じ形で四つ目の引数に1ページのナビゲーションのリンクの数を書く。
初期値は10で、pages_per_setでも変更ができる。

foreach my $current_page (1..6){
    my $pageset = DataPageSet->new(
        $total_entries,
        $entries_per_page,
        $current_page,
        $pages_per_set
    );
    print join ",", "p$current_page",map {sprintf("% 3s",($_ == $current_page) ? "*$_" : $_)} $pageset->pages_in_set;
    print "\n";
}


これの結果は

p1, *1,  2,  3,  4
p2,  1, *2,  3,  4
p3,  2, *3,  4,  5
p4,  3, *4,  5,  6
p5,  3,  4, *5,  6
p6,  3,  4,  5, *6  

となって思った通り動いてくれました。


もうすこしましな実装と、Time::Piece::MySQLのようにプラグイン的な使い方ができたらいいかなぁと思う。