2007年06月25日

PEAR::Package::Pager を使わずphpでページング処理ができるpager()関数 この記事をはてなブックマークに追加

こんにちは、トカちゃんこと、渡嘉敷勝男です(嘘です)。

先日作成した Flickr 日本語検索 では、検索結果が多い場合にページ別に分けて結果を表示している。
通常検索エンジンで検索した際、その検索結果が多い場合に、「 1 2 3 4 5 6 7 8 9 10 次へ>」のようなナビゲーションがされるが、まあ、これと殆ど同じよーなものである。

phpでの実装方法がよくわからなかったのだがどうやら Pear::Pagerの使い方 (Nega Diary) を見ると PEAR :: Package :: Pager を利用するのがよろしいっぽい。
しかし、レンタルサーバなどで、気軽に PEAR ライブラリを入れられない場合は自前で実装する必要があるよーである(うーむ)

で、いろいろ探していたら、教えてgooにそのものズバリの質問があった。

ふむふむ。これを見てみると yambejpさん の回答が例を交えてなかなかわかりやすく書かれていたのでこの方法を採用してみた。

ただし、上記の方法だと、ページを区切る数などが固定されているため yambejpさん の作成されたpager()関数を(若干)汎用的にしてみた。

ちなみに改良してみたpager()関数が下記になる。


<?php
// pager()関数関連の定義
// ページ情報となる変数名の設定
define(PAGE_VALUE "id");
// ページをいくつで区切るか?
// 100件ごとに区切りたいなら100を指定
define(PER_PAGE,10);
// ページメニューの数の幅
// ページ総数 59 / VIEW_PAGE_MENU_WIDTH = 5の場合
// 26ページ目を表示しているときは下記のように表示される
// < 21 22 23 24 25 26 27 28 29 30 31 >
// 26 の前5つ分(21まで)と後ろ5つ分(31まで)を表示する
define(VIEW_PAGE_MENU_WIDTH 5);
// 前に戻るときのマーク 「< (&lt;)」とか「前」とか「←」とか指定
define(PREV_MARK "&lt;");
// 次に行くときのマーク 「< (&gt;)」とか「次」とか「→」とか指定
define(NEXT_MARK "&gt;");
// pager()関数ここから
function pager($idname,$countRe){
    
// 現在のページ情報を取得
    
$id =$_GET[$idname];
    
// ページ情報以外のパラメータを補完し、$other_paramにセット
    
foreach($_GET as $key => $value){
        
// $idname 以外のGETメソッドのパラメータを $other_param に再構築
        
if ($key != $idname){
            
// リンクさせるためにurlencodeする.
            
$other_param .= "&".$key."=".urlencode($value);
        }
    }
    
// ページ数の指定がなければページ数を1にセット
    
if($id==""$id=1;
    
// 最終ページを計算する
    // 総Hit数をページング単位で割ると総ページ数が計算される
    // ページング単位PER_PAGEを10とすると最終ページ数$maxPageは
    // 総Hit数 / PER_PAGEを切り上げたものとなる。
    // 検索結果205件 PER_PAGE:10の場合 $maxPage = ceil(205 / 10) = 21
    
$maxPage=ceil($countRe PER_PAGE);
    
// maxPage=1の場合(PER_PAGEより少ない検索結果)
    // 選択したページ数(id)よりもmaxPageが小さいときはfalse
    
if( ($maxPage == 1) or ($maxPage $id) ) return false;
    
// 選択したページ数がVIEW_PAGE_MENU_WIDTHより大きいとき
    
if($id VIEW_PAGE_MENU_WIDTH 1){
        
// スタートページは選択ページからVIEW_PAGE_MENU_WIDTHを
        // 引いたもの程度出せばよい
        
$startPage $id VIEW_PAGE_MENU_WIDTH;
        
// より小さいもの「 < 」の部分はさらにその$startPageから1を引いたもの
        
$startMore "<a href=\"$PHP_SELF?".$idname."=".($startPage 1).$other_param."\">".PREV_MARK." </a>";
    }else{
        
// そうでなければ$startPage=1(一番はじめのページ = 1)になる
        
$startPage 1;
    }
    
// 選択したページ数に5を足したものより、更に$maxPage
    // (最大ページ)が大きければ
    
if($id VIEW_PAGE_MENU_WIDTH $maxPage){
        
// 表示される最終ページは選択したページに
        // VIEW_PAGE_MENU_WIDTHを足したものになる
        
$endPage $id VIEW_PAGE_MENU_WIDTH;
        
$endMore " <a href=\"$PHP_SELF?".$idname."=".($endPage 1).$other_param."\"> ".NEXT_MARK."</a>";
    }else{
        
// そうでなければ、$endPageは最終ページになる
        
$endPage $maxPage;
    }
    
// ここまでで、計算されたのは
    // $id        : 現在選択しているページ番号
    // $maxPage   : 検索の最終ページ
    // $startPage : 表示されるはじめのページ
    // $endPage   : 表示される最後のページ
    // $page_footer="" として$page_footerを初期化
    
$page_footer="";
    
// $startPage から $endPageまで繰り返し
    
for($i $startPage $i <= $endPage $i++){
        
// $i = $idだったら選択されたページなので、fontsizeを大きくする
        // その際、リンクはなし
        // そうでなければ、id=$iをリンクにセットする
        
$page_footer.= " ".(($id==$i)?"<span style='font-Size:120%'>$i</span>":"<a href=\"$PHP_SELF?$idname=$i$other_param\">$i</a>");
    }
    
// $startMoreと$endMoreを$page_footerの前後に付け足す
    
$page_footer $startMore.$page_footer.$endMore;
    
// $page_footerを表示
    
print $page_footer;
}
// pager()関数ここまで

?>



yambejpさん の作成された元のpager()関数がよくできているため、若干の修正でできたよーである。

利用する際はページ用の変数として利用する変数名と総ページ数を渡せば自動的にpager()関数が変数名を_GET[]から探してきて、リンクを作成するよーになっている。

ページ変数がidで総ページ数が420件の場合は

pager( "id" ,420);


のように指定すればよい。

勿論DBなどと連携する際は、総ページ数を取得したり、一覧を表示させたりするのはきちんと php で書かなきゃだめですよ。

うーむ、なかなか便利ばい。

posted by りょーち at 10:57 | Comment(16) | TrackBack(1) | Web周辺技術
この記事へのコメント
はじめまして、こんにちは。
PHP初心者のゆうひです。
とても恥ずかしいことですが
この関数・DB接続(MySQL)・関数を呼び出すタイミングがわかりません。
教えていただけませんでしょうか。
Posted by ゆうひ at 2007年07月25日 16:53
こんにちは、りょーち@管理人です。
この関数は、DBとは全く関係なく動作します。
pager($idname,$countRe){}では
$idnameはページ変数
$countReは総ページ数
となります。

作り方にもよりますが、
検索総数はSQLのselectとcountで総数が取得できますよね。
それを事前に取得し、変数$totalに入れておきます。

この検索結果はGETメソッドで
index.php?page=2&searchword=abc&lang=ja
などと表現している場合は、
pager(”page”,$total);
などとすれば、よいはずです。
なので、$total(総数)については自分で求めることになります。

関数を呼び出すタイミングはそのHTML内でページャー文字列を記載したい場所となります。

いかがでしょうか?
Posted by りょーち at 2007年07月25日 18:18
早いうえに丁寧に教えていただきありがとうございます。
すごくわかりやすかったです。
今、再度試みたのですが、関数を呼び出すと
画面がまっしろになってしまいます。
本番環境がLinuxだからというのは関係ないものでしょうか。

質問ばかりでごめんなさい。
(先ほどの2重投稿もごめんなさい。)
Posted by ゆうひ at 2007年07月26日 00:48
ゆうひさん、こんにちは。りょーち@管理人です。
>関数を呼び出すと画面がまっしろになってしまいます。

まっしろというのは、どういうことでしょう?
PHPにより生成されたHTMLファイルのソースはどうなっていますか?
関数呼び出し前後にprint文などで変数を確認してみてはどうでしょう?

この情報だけではよくわからないです。
動かそうとしているソースをみせてもられれば何らかの解決方法は見出せそうな気がします。
Posted by りょーち at 2007年07月26日 01:28
関数の呼び出しに誤りがありました。
わたしの不注意でした。すみません。

DBから1ページ目を取得するところまで
できました。
あと一息って感じです。
(ここまでがあまりも長すぎました 汗)

Posted by ゆうひ at 2007年07月26日 02:09
おかげさまで、ページングがうまくいきました。
これからもこの関数を大事に使わせていただきます。
ありがとうございました。
Posted by ゆうひ at 2007年07月26日 12:52
無事に利用できて何よりです。
より素晴らしい機能などがありましたら、どんどん機能追加してみてください。

ではでは。
Posted by りょーち at 2007年07月26日 16:53
はじめまして。こちらの関数を大変便利に使わせていただいています。
一つ質問なのですが、

Yahooの検索結果のように
○○〜○○件目

という感じで表示しているデータの位置?を
取得し表示させたいのですが、何かよい方法がありましたら教えて頂けないでしょうか?

少し前の記事にコメントして申し訳ございませんm(__)m
Posted by soma at 2007年12月11日 01:55
somaさんこんにちは。りょーち@管理人です。
ご返信遅れまして申し訳ありませんでした。

うーむ、何かできそうな予感はしますね。

10件づつ表示する場合、検索結果が45件だった場合は
1ページ目: 1〜10
2ページ目:11〜20
3ページ目:21〜30
4ページ目:31〜40
5ページ目:41〜45
なので、
$endPage(最終ページ)でない場合は
($id-1)*10+1 〜 $id*10
$endPage(最終ページ)のときは
($id-1)*10+1 〜 $countRe
でよさそうな気がします。
あとはこれをechoすればよいかと・・・

今、時間がないのでプログラムで検証していませんが、基本的な考え方はこれでよさそうな気がします。
Posted by りょーち at 2007年12月11日 18:37
somaさん、こんにちは。りょーち@管理人です。
ご期待にお答えできているかどうかわかりませんが、ご要望に近いと思われる機能を作って見ました。
下記をご確認ください。
http://ryouchi.seesaa.net/article/72446915.html
ご参考まで。
Posted by りょーち at 2007年12月12日 17:45
はじめましてこんにちは。
まったく持ってお恥ずかしいお話なんですが、PHP等の超初心者なんですがりょーちさんがお話しているページングが私のやりたいことなんですがまったくわかりません。ajaxで動かすプログラムのどこに入れたらいいのか全然わからないのです。お力になってもらえませんか。
Posted by じみー at 2008年07月20日 09:53
じみーさん、こんにちは。りょーち@管理人です。
お力になりたいのは山々ですが、上記の情報だけではまったくどうしてよいかわかりません。
先ず問題を整理することをおすすめします。
どこまでできていてどこがわからないのか?
これがわからないとなんともお答えするのが難しいです。
ご検討のほど、よろしくお願いいたします。
Posted by りょーち at 2008年07月22日 08:48
お返事ありがとうございます。自分でも何をどう話したらいいのかよくわからないのです。下記のような場合はどのようにすればいいのでしょうか。

【php側】

template_dir = "../../templates/rakuten";
$smarty->config_dir = "../../config/rakuten";
$smarty->plugins_dir = array("./plugins", "../common/plugins");
$smarty->compile_dir = "../../templates_c/rakuten";
$smarty->cache_dir = "../../cache/rakuten";
$smarty->caching = 2;
$smarty->cache_lifetime = 43200;

$tmpl_name = check_template($smarty);
$smarty->config_load("tmpl.cfg", "id");
$developer_id = $smarty->get_config_vars('developer_id');
$affiliate_id = $smarty->get_config_vars('affiliate_id');
$smarty->assign('developer_id', $developer_id);
$smarty->assign('affiliate_id', $affiliate_id);

$cache_id = 'rakuten:' . serialize($_GET);
if (!$smarty->is_cached($tmpl_name, $cache_id)) {
// 楽天の情報を取得
$query = "http://api.rakuten.co.jp/rws/1.7/rest?developerId=${developer_id}&operation=ItemSearch&version=2007-04-11";
if ($affiliate_id) {
$query .= "&affiliateId=${affiliate_id}";
}
$query .= "&keyword=" . urlencode($_GET['keyword']);
$max = intval($_GET['max']);
if ($max > 0 && $max <= 30) {
$query .= "&hits=${max}";
}
if (isset($_GET['sort'])) {
$query .= "&sort=" . urlencode($_GET['sort']);
}
if (intval($_GET['genreId'])) {
$query .= "&genreId=" . intval($_GET['genreId']);
}
if ($_GET['field'] == 'use') {
$query .= "&field=0";
}
if (intval($_GET['imageFlag']) == 1) {
$query .= "&imageFlag=1";
}
if (intval($_GET['minPrice']) > 0) {
$query .= "&minPrice=" . intval($_GET['minPrice']);
}
if (intval($_GET['maxPrice']) > 0) {
$query .= "&maxPrice=" . intval($_GET['maxPrice']);
}
if ($_GET['orFlag'] == 'use') {
$query .= "&orFlag=1";
}
$client =& new HTTP_Client();
$client->get($query);
$resp =& $client->currentResponse();
if ($resp['code'] != 200) {
$smarty->assign('tid', intval($_GET["tid"]));
$smarty->assign('is_error', 1);
$smarty->assign('is_connection_error', 1);
$smarty->assign('errmsg', '接続に失敗しました。');
$smarty->caching = 0;
$smarty->display('error.tpl');
exit();
}

//print_r($resp['body']);
//exit();

$xml = new XML_Unserializer();
$xml->setOption('complexType', 'array');
$xml->setOption('forceEnum', array('Item'));
$result = $xml->unserialize($resp['body'], FALSE);
$data = $xml->getUnserializedData();

$status = $data['header:Header']['Status'];

if ($status == 'ClientError' ||
$status == 'ServerError' ||
$status == 'Maintenance') {
$smarty->assign('tid', intval($_GET["tid"]));
$smarty->assign('is_error', 1);
$smarty->assign('is_api_error', 1);
$smarty->assign('api_errmsg', $data['header:Header']['StatusMsg']);
$smarty->caching = 0;
$smarty->display('error.tpl');
exit();
}

// print("query = $query\n");
// print_r($data);
// exit();

$smarty->assign('total_count', $data['Body']['itemSearch:ItemSearch']['count']);
$smarty->assign('items', $data['Body']['itemSearch:ItemSearch']['Items']['Item']);
}

// テンプレートの表示
$smarty->display($tmpl_name, $cache_id);

?>

【tpl側】
{show_bom}
{if $total_count > 0}
{items}

{if $is_image}{/if}
{$title|mb_truncate:100:" ..."}
{$price|number_format}円
({$shop_name|mb_truncate:100:" ..."})

{/items}
{else}{* if $total_count > 0 の条件を満たさない場合 *}
検索条件に合う商品はありません。
{/if}{* if $total_count > 0 *}

【html側】



Posted by じみー at 2008年07月27日 21:31
ページ変数がidで総ページ数が420件の場合は
pager( "id" ,420);
と指定します。
総ページ数とページ変数は何かを理解すれば、「phpによるページング処理時のメニューサンプル」を見ることで呼び出しのタイミングなどが分かると思います。
どうでしょうか?
Posted by りょーち at 2008年07月28日 06:44
初めまして。
PhpとMySQlを使ってデータを表示する時の
ページジングの処理で、
PEARライブラリを使用しない方法をサガしていたらこちらに着きました。
記事のプログラムを使ってみました。
DB検索で、少々考える部分がありますが、
とても便利です。
Posted by しゅうじ at 2009年06月10日 16:04
こんにちは。りょーち@管理人です。
コメントありがとうございます。

私もよくわからない部分が多いので、イロイロ改造してみてください。
いいのができたら是非教えてくださいね。

ではでは。
Posted by りょーち at 2009年06月10日 18:16
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.seesaa.jp/tb/45846102

use Simple API for trackback sites image
youtubo運用ブログ ページング
Excerpt: 検索結果の下にある1・2・3・・・と次のページや指定のページへリンクする機能をページングと言うそうです。呼び名そのものは知ってはい...
Weblog: youtubo運用ブログ
Tracked: 2008-02-13 15:50