如何对长列表进行分页

时间:2018-10-08 06:40:09

标签: perl pagination catalyst paginate paginator

我(仍)在学习Perl的Catalyst框架。而且我需要翻阅一长串书。我不知道该使用什么来分页以及如何使用它。其次,如果我想将“ page = 1”参数传递给我的列表方法(现在以它的形式),则传递的值将不正确。如果我将Args的数量修改为1,则它会抱怨找不到页面。

这些是我的文件:

Book.pm (ORM文件)

use utf8;
package Library::Schema::Result::Books;

use strict;
use warnings;

use Moose;
use MooseX::NonMoose;
use MooseX::MarkAsMethods autoclean => 1;
extends 'DBIx::Class::Core';

__PACKAGE__->load_components("InflateColumn::DateTime");
__PACKAGE__->table("books");
__PACKAGE__->add_columns(
  "id",
  {
    data_type => "uuid",
    default_value => \"uuid_generate_v4()",
    is_nullable => 0,
    size => 16,
  },
  "title",
  { data_type => "varchar", is_nullable => 0, size => 128 },
);

__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("uk_books", ["title"]);
__PACKAGE__->meta->make_immutable;

1;

Book.pm (控制器)

package Library::Controller::Book;
use Moose;
use namespace::autoclean;
use utf8;
use Data::Validate::UUID qw(is_uuid);

BEGIN { extends 'Catalyst::Controller'; }

sub base :Chained('/'): PathPart('book'): CaptureArgs(0) {
    my ($self, $c) = @_;
    $c->stash(books_rs => $c->model('DB::Books'));
    $c->stash(books => [$c->stash->{books_rs}->search(
        {},
        {order_by => 'title ASC'})]
    );
}

sub list :Chained('base'): PathPart('list'): Args(0) {
    my ($self, $c) = @_;
    $c->stash(template => 'book/list.tt2');
}

sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    return $c->res->redirect(
        $c->uri_for($c->controller('Book')->action_for('list'))
    );
}

sub book :Chained('base'): PathPart(''): CaptureArgs(1) {
    my ($self, $c, $bookid) = @_;
    if(!is_uuid(uc($bookid))) {
        die "Invalid book ID.";
    }
    my $book = $c->stash->{books_rs}->find(
        { id => $bookid },
        { key => 'primary' }
    );
    die "No such user" if(!$book);
    $c->stash(book => $book);
}

sub add :Chained('base'): PathPart('add'): Args(0) {
    my ($self, $c) = @_;
    if(lc $c->req->method eq 'post') {
        my $params = $c->req->params;
        my $books_rs = $c->stash->{books_rs};
        my $newbook = $books_rs->create({
            title => $params->{newBookTitle},
        });
        return $c->res->redirect(
            $c->uri_for($c->controller('Book')->action_for('list')
        ));
    }
}

sub edit :Chained('book') :PathPart('edit'): Args(0) {
    my ($self, $c) = @_;
    if(lc $c->req->method eq 'post') {
        my $params = $c->req->params;
        my $book = $c->stash->{book};
        $book->update({
            title => $params->{title},
        });
        return $c->res->redirect( $c->uri_for(
            $c->controller('Book')->action_for('list'),
            [ $book->id ]
        ));
    }
}

sub remove :Chained('book'): PathPart('remove'): Args() {
    my ($self, $c) = @_;
    my $book = $c->stash->{book};
    $book->delete();
    return $c->res->redirect(
        $c->uri_for($c->controller('Book')->action_for('list'))
    );
}

__PACKAGE__->meta->make_immutable;

1;

以及我的 books / list.tt 文件中的相关部分:

<table>
    <thead>
        <tr>
            <th></th>
            <th>Book title</th>
        </tr>
    </thead>
    <tbody>
        [% FOREACH book IN books -%]
        <tr>
            <td>
                <a href="[%- c.uri_for(c.controller('Book').action_for('remove'), [book.id]) %]">
                    <img src="../../images/trash.png" width="22" height="22">
                </a>
            </td>
            <td>[% book.title %]</td>
        </tr>
        [% END -%]
    </tbody>
</table>

2 个答案:

答案 0 :(得分:0)

要使其正常工作,您需要做两件事。总体而言,这很简单。 DBIC Cookbook中对此进行了说明。

控制器

您的list方法只列出了未分页的完整图书清单,这些清单藏在您的base链接方法中。现在,您需要添加代码以采用URL参数并减少列表。

sub list :Chained('base'): PathPart('list'): Args(0) {
    my ($self, $c) = @_;

    if (my $page = $c->req->params->{page}) {
        # TODO: validate $page

        my $rs = $c->stash->{books};
        $c->stash->{books} = $rs->search(undef, {
             page => $page,
             rows => 10,    # or how many you want
        });
    }

    $c->stash(template => 'book/list.tt2');
}

此代码将用一个新的结果集替换保存的结果集,该结果集已附加LIMIT。请记住,结果集可以链式堆叠,因此,每将一个新的->search都称为结果集对象,就会返回一个进一步深入的新结果集。直到稍后在模板中使用SQL时,才在列表上下文中使用SQL,这意味着需要调用->all

无需为显示较小列表而对模板进行任何更改。

但是,您可能希望控制分页。您可以使用Data::Page对象来实现。您的结果集可以方便地为您提供此结果。但是,这要付出额外的COUNT查询的代价。如果您对正在发生的事情感兴趣,请打开DBIC_TRACE=1环境变量以查看在后台运行的查询。

在您刚刚添加上述代码的list方法中,也隐藏了寻呼机。

my $rs = $c->stash->{books};
$c->stash->{books} = $rs->search(undef, {
     page => $page,
     rows => 10,    # or how many you want
});

$c->stash->{pager} = $rs->pager;

模板

现在,我们需要在模板中显示一些控件。我不会全部显示,只给您一个想法。由于我们仍支持完整列表,因此只有在有寻呼机的情况下,我们才能显示控件。

<table>
    [%# ... %]
    <tbody>
        [% FOREACH book IN books -%]
        <tr>
            [%# ... %]
        </tr>
        [% END -%]
    </tbody>
</table>

[% IF pager %]
<ul>
    <li><a href="?page=[% pager.first_page %]">First page</a></li>
</ul>
[% END %]

这里不需要使用c.uri_for,因为我们要做的就是添加URL参数。用户的浏览器足够聪明,可以使仅包含参数的相对URL指向已经存在的相同内容。因此,如果用户正在查看https://example.org/list?page=2,则单击首页链接?page=1实际上会将他们带到https://example.org/list?page=1

答案 1 :(得分:0)

我设法摆脱了该非页面错误:

sub list :Chained('base'): PathPart('list'): Args(0) {
    my ($self, $c) = @_;
    if (my $page = $c->req->params->{page}) {
        my $rs = $c->stash->{books_rs}->search({}, {
             page => $page,
             rows => 5,
        });
        $c->stash->{books_rs} = $rs;
        $c->stash->{pager} = $rs->pager;
    }
    $c->stash(template => 'book/list.tt2');
}

,但仍显示所有书籍。使用 page 参数传递的值对结果集没有影响。