先日作成した 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);
// 前に戻るときのマーク 「< (<)」とか「前」とか「←」とか指定
define(PREV_MARK , "<");
// 次に行くときのマーク 「< (>)」とか「次」とか「→」とか指定
define(NEXT_MARK , ">");
// 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 で書かなきゃだめですよ。
うーむ、なかなか便利ばい。
PHP初心者のゆうひです。
とても恥ずかしいことですが
この関数・DB接続(MySQL)・関数を呼び出すタイミングがわかりません。
教えていただけませんでしょうか。
この関数は、DBとは全く関係なく動作します。
pager($idname,$countRe){}では
$idnameはページ変数
$countReは総ページ数
となります。
作り方にもよりますが、
検索総数はSQLのselectとcountで総数が取得できますよね。
それを事前に取得し、変数$totalに入れておきます。
この検索結果はGETメソッドで
index.php?page=2&searchword=abc&lang=ja
などと表現している場合は、
pager(”page”,$total);
などとすれば、よいはずです。
なので、$total(総数)については自分で求めることになります。
関数を呼び出すタイミングはそのHTML内でページャー文字列を記載したい場所となります。
いかがでしょうか?
すごくわかりやすかったです。
今、再度試みたのですが、関数を呼び出すと
画面がまっしろになってしまいます。
本番環境がLinuxだからというのは関係ないものでしょうか。
質問ばかりでごめんなさい。
(先ほどの2重投稿もごめんなさい。)
>関数を呼び出すと画面がまっしろになってしまいます。
まっしろというのは、どういうことでしょう?
PHPにより生成されたHTMLファイルのソースはどうなっていますか?
関数呼び出し前後にprint文などで変数を確認してみてはどうでしょう?
この情報だけではよくわからないです。
動かそうとしているソースをみせてもられれば何らかの解決方法は見出せそうな気がします。
わたしの不注意でした。すみません。
DBから1ページ目を取得するところまで
できました。
あと一息って感じです。
(ここまでがあまりも長すぎました 汗)
これからもこの関数を大事に使わせていただきます。
ありがとうございました。
より素晴らしい機能などがありましたら、どんどん機能追加してみてください。
ではでは。
一つ質問なのですが、
Yahooの検索結果のように
○○〜○○件目
という感じで表示しているデータの位置?を
取得し表示させたいのですが、何かよい方法がありましたら教えて頂けないでしょうか?
少し前の記事にコメントして申し訳ございませんm(__)m
ご返信遅れまして申し訳ありませんでした。
うーむ、何かできそうな予感はしますね。
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すればよいかと・・・
今、時間がないのでプログラムで検証していませんが、基本的な考え方はこれでよさそうな気がします。
ご期待にお答えできているかどうかわかりませんが、ご要望に近いと思われる機能を作って見ました。
下記をご確認ください。
http://ryouchi.seesaa.net/article/72446915.html
ご参考まで。
まったく持ってお恥ずかしいお話なんですが、PHP等の超初心者なんですがりょーちさんがお話しているページングが私のやりたいことなんですがまったくわかりません。ajaxで動かすプログラムのどこに入れたらいいのか全然わからないのです。お力になってもらえませんか。
お力になりたいのは山々ですが、上記の情報だけではまったくどうしてよいかわかりません。
先ず問題を整理することをおすすめします。
どこまでできていてどこがわからないのか?
これがわからないとなんともお答えするのが難しいです。
ご検討のほど、よろしくお願いいたします。
【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側】
pager( "id" ,420);
と指定します。
総ページ数とページ変数は何かを理解すれば、「phpによるページング処理時のメニューサンプル」を見ることで呼び出しのタイミングなどが分かると思います。
どうでしょうか?
PhpとMySQlを使ってデータを表示する時の
ページジングの処理で、
PEARライブラリを使用しない方法をサガしていたらこちらに着きました。
記事のプログラムを使ってみました。
DB検索で、少々考える部分がありますが、
とても便利です。
コメントありがとうございます。
私もよくわからない部分が多いので、イロイロ改造してみてください。
いいのができたら是非教えてくださいね。
ではでは。
pagerを使わせていただいてます。
未定義の変数っていうエラーが表示されるのですが、どう解決すればいいかわからなくて、質問させて頂いてます。
Notice: Undefined variable: other_param in /var/www/html/immovables/html/bukken/pager.php on line 27
Notice: Undefined variable: PHP_SELF in /var/www/html/immovables/html/bukken/pager.php on line 75
Notice: Undefined variable: startMore in /var/www/html/immovables/html/bukken/pager.php on line 78
Notice: Undefined variable: endMore in /var/www/html/immovables/html/bukken/pager.php on line 78
pagerを使うページでincludeして使おうとしてますが、なかなかうまくいかずです。
宜しくお願いします。
若しくはerror_reporting(E_ERROR | E_WARNING | E_PARSE);
とかにしてみるのはどうでしょう?
http://jp.php.net/manual/ja/function.error-reporting.php
php mysqlで条件をつけて検索しております。
検索をかけると、1ページ目はうまく表示されるのですが、2ページ以降で、総数が0となってしまい、ページの移動?データの橋渡しがうまくいきません。
お手数ですが、ご教授いただけたらと思います。
どうかよろしくお願いします。
このプログラムはあくまでもページング処理だけのプログラムなのでSQL文の検索部分はご自身で記載いただく必要があります。
この記事の2番目のコメントと以下のURLもご参考に。
http://ryouchi.seesaa.net/article/72446915.html
情報が足りなくてすいません…
件数を数える時に
//ページング処理
//総レコード数を取得する
//条件がある場合は、where 条件式を書く
$sql = "SELECT COUNT(*) AS reccnt FROM test";
$res = mysql_query($sql, $conn) or die ("データ抽出エラー");
$row = mysql_fetch_array($res, MYSQL_ASSOC);
$total = $row["reccnt"];
$_GET['id']<>"" ? $page = $_GET['id'] : $page = 1;
function print_value($array){
echo '';
foreach ($array as $key => $value){
echo $key." => ".$value."";
}
}
// 以下テストパターン
$perpage = PER_PAGE;
echo "検索結果:"."$total "."件 / ".PER_PAGE."ページ毎にページング";
// 総ページ数からPAGER関数を呼び出す
echo 'pager( "'.PAGE_VALUE.'" ,'."$total ".');';
pager( PAGE_VALUE , "$total ");
print_value($_GET);
echo "";
だとうまくいくのですが、件数を求めるときに、
$sql = "SELECT COUNT(*) AS reccnt FROM search where 〜";
のように条件をつけると、2ページ目の$total(総数)が0になってしまうのです。
検索結果:5件 / 2ページ毎にページング
pager( "id" ,5);
1 2 3
が
検索結果:0件 / 2ページ毎にページング
pager( "id" ,0);
id => 2
このような感じです。
いろいろ試してみたのですが、なかなかうまくいきません。
お手数ですが、よろしくお願いします。
「このプログラムはあくまでもページング処理だけのプログラムなのでSQL文の検索部分はご自身で記載いただく必要があります。」
pager()関数の引数に正しい数値が入っていますか?
呼び出し前にechoしてみてください。
検索をかけて、引っ張ってきた$_POSTのデータを2ページ目以降に引き渡せていないのが原因だと思うのですが、なかなか解決しません。
何度も申し訳ありません。
GETメソッドで渡してください。
$id =$_GET[$idname];
でどの変数名がページ番号かを判定しています。
それ以外のGETメソッドで渡されたパラメータは$other_paramとして連結します。
このためPOSTで渡すと動作しません。
POSTで渡すなら当該部分を修正する必要があります。
PHPはじめたばかりの初心者です。
現在サンプルを元に作成をしております。
indexで前後ページへのリンクを表示するのは出来ているのですが、そこをクリックしてもそのページの情報を表示することができません。(LIMITで範囲は指定済)
常に最初の1ページの情報のみ表示されてしまうのですが、どういった書き方をすれば前後ページの情報を表示させることができるのでしょうか?
本当に初歩的な質問で申し訳ございません。
ちなみにここのサンプルを使って作成中です。
http://3d.rokujyou.com/cake/sample5.html
>常に最初の1ページの情報のみ表示されてしまうのですが
につきましてはこのpager()関数ではどうすることもできません。
「このプログラムはあくまでもページング処理だけのプログラムなのでSQL文の検索部分はご自身で記載いただく必要があります。」
逆にSQLに関する記述がなくても「ページ変数」と「総ページ数」だけ指定すれば動きます。
ページ変数と総ページ数、どのように書いたらよいのでしょうか?
もしくは参考になるサイトやページなどございましたら教えていただけたら幸いです。
ちなみにコントローラーでは
function index($page=1){
$count = $this->Urllist->findCount();
$this->set('count', $count);
$before = "";
if($page >= 2){
$before = $page - 1;
}
$this->set('before', $before);
$after = "";
if($page*LIMIT < $count){
$after = $page + 1;
}
$this->set('after', $after);
$this->set('urllists', $this->Urllist->findAll(null,null,null,LIMIT,$page));
}
として前後ページのリンクを表示しております。
はじめの
define(PAGE_VALUE , ”id”);
define(PER_PAGE,10);
で定義しているのがページ変数です。
http://www.usamimi.info/~ryouchi/pager/?id=5&hidden_1=alpha&hidden_2=beta&word1=%E6%B5%B7%E3%81%AE%E7%94%9F%E3%81%8D%E7%89%A9&word2=%E9%AD%9A%E3%81%9F%E3%81%A1の場合、idがページ変数になります。
総ページ数は今考えるとちょっと違いますね。総ページ数ではなくて検索にヒットした総数ですね。
総数$countRe個をPER_PAGE毎に区切ってページングとなります。
検索結果42件を10ページ毎に区切ると
1 2 3 4 5
ってな感じになります。
前後ページのリンクはここでは考慮していません。
というか、getメソッドで送られるパラメータの中からページ部分だけ取り出してリンクを生成するのがこのプログラムです。
はじめの
define(PAGE_VALUE , ”id”);
って、宣言したは良いものの、以後どこで使われているんでしょう?
「あれ〜?この定数、どこにも使われてないよな〜・・・」
って思ってるんですが。
ちなみにPHP初心者ですので、何か勘違いしてたらすみませんです。
culdraさん、こんにちは。りょーち@管理人です。
コメント頂きましてありがとうございます。
>はじめの
>define(PAGE_VALUE , ”id”);
>って、宣言したは良いものの、以後どこで使われているんでしょう?
pager関数を呼び出すサンプルとして
pager( "id" ,420);
と記載していますが、実際は
pager(PAGE_VALUE,420);
って感じで記載しようと思っていました。
こうしておけば、define部分のPAGE_VALUEを変えればすぐに対応できそうかなと・・・
まあ、複数のidを使い分けるような場合もあるのかもしれないので、あまり意味ないかもしれないと思い始めてしまいました。
ではでは。
これで疑問が解決致しました。
ありがとうございました!
PHPでのページングについてご教授下さい。
items = array_slice($rss->items, 0, 5);
foreach ($rss -> items as $item ) {
$pdate = ($item["pubdate"]) ? $item["pubdate"] : $item["dc"]["date"];
$entry[] = array(
"utime" => strtotime($pdate),
"title" => mb_convert_encoding($item["title"], "UTF-8", "auto"),
"url" => $item["link"],
"site" => $rss -> channel['title'],
);
}
}
rsort($entry);
$count = count($entry);
for ($i = 0; $i < $count; $i++) {
echo ''.date("n年d日", $entry[$i]["utime"]).' '.$entry[$i]["title"].' '.$entry[$i]["site"].'';
}
?>
上記のようなソースで複数のRSSフィードをサイト上に表示しているのですが、フィード数が増えてきたので、ページングを使用して10件ずつ表示しようとし、色々なサイトを調べているのですが皆目見当がつきません。
もしよろしければ、お時間のある時にでもアドバイスを頂けませんでしょうか?
よろしくお願い致します。
以前のコメントでも記載していますが、このプログラムは「GETメソッドで送られるパラメータの中からページ部分だけ取り出してページ遷移用のリンクを生成する」ものです。ですので実際のページ分割処理は自身のプログラムに委ねられます。
WHTさんのプログラムを見てみると配列$entryをarray_multisort()などでソート後指定の個数を取得するようにすればよさそうな気がします。