Perl: Разбивка по страницам 2 (mysql)
Я уже писал ранее про "разбивку по страницам", но этот метод имеет один существенный недостаток: если мы работаем с базой данных огромных размеров, то, во-первых теряется много времени на выборку всех записей, а во-вторых - может не хватить памяти для хранения выбранных результатов перед тем, как мы их отдадим на съедение split_page
Поэтому я создал новую версию: split_page_mysql
К сожалению в нем пришлось отказаться от параметра reverse и page=all
Использование и синтаксис вызова:
Перед началом работы получаем количество записей в таблице:
Потом получаем из CGI номер текущей страницы
Передаем в метод split_page_mysql() общее количество записей $count, и на выходе получаем $limit, $list_page, $show_page
где:
$limit - хэш-массив, содержащий два ключа: limit_offset и limit_rows
$list_page - список номеров страниц
$show_page - регистр показа/скрытия ссылок на страницы
Параметры split_page_mysql()
Последующий выбор из таблицы
После отработки метода проверяем переменную $limit , и если она определена, то к запросу к таблице добавляем LIMIT offset, rows
А вот и сам Perl-метод split_page_mysql(), который это всё реализует:
Поэтому я создал новую версию: split_page_mysql
К сожалению в нем пришлось отказаться от параметра reverse и page=all
Использование и синтаксис вызова:
Перед началом работы получаем количество записей в таблице:
my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM table");
Потом получаем из CGI номер текущей страницы
my $page = CGI->param('page') || 0;
Передаем в метод split_page_mysql() общее количество записей $count, и на выходе получаем $limit, $list_page, $show_page
где:
$limit - хэш-массив, содержащий два ключа: limit_offset и limit_rows
$list_page - список номеров страниц
$show_page - регистр показа/скрытия ссылок на страницы
my ($limit, $list_page, $show_page) = split_page_mysql({
list_records => $count,
page => $page,
count_records_page => 5,
page_left_right => 3
});
Параметры split_page_mysql()
- list_records
общее количество записей в таблице
- page
номер страницы
- count_records_page
количество элементов на одной странице
- page_left_right
отображение ссылок на страницы - сколько видимых номеров страниц будет от текущей- пример 1
у нас 20 страниц. page_left_right=3. текущая страница номер 1
визуально это будет так:
страницы: 1 2 3 ... 20
- пример 2
у нас 20 страниц. page_left_right=5. текущая страница номер 12
визуально это будет так:
страницы: 1 ... 7 8 9 10 11 12 13 14 15 16 17 ... 20
- пример 1
Последующий выбор из таблицы
После отработки метода проверяем переменную $limit , и если она определена, то к запросу к таблице добавляем LIMIT offset, rows
if ($limit) {
$dbh->prepare("SELECT * FROM table LIMIT " . $limit->{limit_offset} . "," . $limit->{limit_rows});
}
Универсальный код для HTML-шаблонов, учитывающий возможный реверс и показывающий все возможности модуля (подходит и для split_page)- для HTML::Template
<tmpl_if show_page> Страницы: <tmpl_loop pages_loop> <tmpl_if PAGE_NUMBER_START><a href="?page=1">первая страница</a></tmpl_if> <tmpl_if PAGE_PREV><tmpl_unless select_all><a href="?page=<tmpl_var PAGE_PREV>">предыдущая страница</a></tmpl_unless></tmpl_if> <tmpl_loop PAGE_START><tmpl_if select_page><tmpl_var page><tmpl_else><a href="?page=<tmpl_var page>"><tmpl_var page></a></tmpl_if> </tmpl_loop> <tmpl_if PAGE_SM>...</tmpl_if> <tmpl_loop PAGE_MEDIUM><tmpl_if select_page><tmpl_var page><tmpl_else><a href="?page=<tmpl_var page>"><tmpl_var page></a></tmpl_if> </tmpl_loop> <tmpl_if PAGE_ME>...</tmpl_if> <tmpl_loop PAGE_END><tmpl_if select_page><tmpl_var page><tmpl_else><a href="?page=<tmpl_var page>"><tmpl_var page></a></tmpl_if> </tmpl_loop> <tmpl_if PAGE_NEXT><tmpl_unless select_all><a href="?page=<tmpl_var PAGE_NEXT>">следующая страница</a></tmpl_unless></tmpl_if> <tmpl_if PAGE_NUMBER_END><a href="?page=<tmpl_var PAGE_NUMBER_END>">последняя страница</a></tmpl_if> <tmpl_unless split_mysql><tmpl_unless select_all><a href="?page=all">все</a><tmpl_else>все</tmpl_unless></tmpl_unless> </tmpl_loop> </tmpl_if>
- для Template::Toolkit
[% IF self.show_page %] Страницы: [% FOREACH page IN self.pages_loop %] [% IF page.PAGE_NUMBER_START %]<a href="?page=1">первая страница</a>[% END %] [% IF page.PAGE_PREV %][% UNLESS page.select_all %]<a href="?page=[% page.PAGE_PREV %]">предыдущая страница</a>[% END %][% END %] [% FOREACH p IN page.PAGE_START %][% IF p.select_page %][% p.page %][% ELSE %]<a href="?page=[% p.page %]">[% p.page %]</a>[% END %] [% END %] [% IF page.PAGE_SM %]...[% END %] [% FOREACH p IN page.PAGE_MEDIUM %][% IF p.select_page %][% p.page %][% ELSE %]<a href="?page=[% p.page %]">[% p.page %]</a>[% END %] [% END %] [% IF page.PAGE_ME %]...[% END %] [% FOREACH p IN page.PAGE_END %][% IF p.select_page %][% p.page %][% ELSE %]<a href="?page=[% p.page %]">[% p.page %]</a>[% END %] [% END %] [% IF page.PAGE_NEXT %][% UNLESS page.select_all %]<a href="?page=<tmpl_var PAGE_NEXT>">следующая страница</a>[% END %][% END %] [% IF page.PAGE_NUMBER_END %]<a href="?page=<tmpl_var PAGE_NUMBER_END>">последняя страница</a>[% END %] [% UNLESS page.split_mysql %][% UNLESS page.select_all %]<a href="?page=all">все</a>[% ELSE %]все[% END %][% END %] [% END %] [% END %]
А вот и сам Perl-метод split_page_mysql(), который это всё реализует:
sub split_page_mysql {
my $self = shift;
my $p = shift || return undef;
my $count_records_total = $p->{list_records} || 'D';
$count_records_total =~ s/\D//;
$count_records_total ||= 0;
my $page = $p->{page}||'D';
$page =~ s/\D//;
$page ||= 1;
my $count_records_page = $p->{count_records_page} || 10;
my $page_left_right = $p->{page_left_right} || 1;
return undef if $count_records_page >= $count_records_total;
my ($page_start, $page_medium, $page_end) = ([], [], []);
my $pE = int($count_records_total/$count_records_page);
$pE++ if ($count_records_total/$count_records_page) > $pE;
$page = $pE if $page >= $pE;
$page_start = [{page=>1, select_page=>($page==1)?1:0}];
$page_end = [{page=>$pE, select_page=>($page==$pE)?1:0}];
my $pSM = ($page-$page_left_right <= 2) ? 0 : 1;
my $pME = ($page+$page_left_right >= $pE-1) ? 0 : 1;
for (my $t=$page-$page_left_right;$t<=$page+$page_left_right;$t++){
if (($t > 1) && ($t < $pE)){
push(@{$page_medium},{
page => $t,
select_page => ($page==$t)?1:0
});
}
}
my $limit = {
limit_offset => ($page-1)*$count_records_page,
limit_rows => $count_records_page,
};
my $page_list = {
PAGE_START => $page_start,
PAGE_MEDIUM => ($page_medium) ? $page_medium : [],
PAGE_END => $page_end,
PAGE_SM => $pSM,
PAGE_ME => $pME,
PAGE_PREV => ($page > 1) ? ($page-1) : 0,
PAGE_NEXT => ($page < $pE) ? ($page+1) : 0,
split_mysql => 1,
};
return $limit, $page_list, ($pE>1)?1:0;
}