xmlからデータを取得(simpleXML)し、ページ分割する方法について教えて下さい - PHPプロ!Q&A掲示板

2815

  • 0P

xmlからデータを取得(simpleXML)し、ページ分割する方法について教えて下さい

質問日時 / 2010年7月19日 17:58 (最終編集:7月19日 23:55)    回答数 / 7件

Questioner:  zakoba  このエントリーをはてなブックマークに追加 

キーワード / ページ分割    simpleXML    ページ送り   

開発環境: PHP Version 5.2.11

iPhone上から「投稿者名・画像・コメント」を投稿し、WEBSITE上でこの内容を一覧表示する際に、10件ごとにページを分割する仕組みの構築を試みておりますが、ページ分割の方法が分からず、こちらに質問させて頂く次第です。

iPhone上では【投稿者名】【画像】【コメント】を投稿する為のGUIを用意しており、画像ファイルと下記構造のxmlファイルをサーバ上に随時アップロードするところまでは実現できております。

  1. <data id="00001">
  2. <userName>山田太郎</userName>
  3. <photoName>img00001.jpg</photoName>
  4. <comment>コメント欄</comment>
  5. </data>
  6.  
  7. <data id="00002">
  8. <userName>山田花子</userName>
  9. <photoName>img00002.jpg</photoName>
  10. <comment>コメント欄</comment>
  11. </data>

●サーバ上に保存されるxmlは1つです。xmlに随時情報が追記されます。
●“id”“photoName”は自動でナンバリングされます。
●“photoName”はiPhone上から投稿された画像に付与されるファイル名と同一です。
●画像ファイルが保存される場所は固定されている為、後の一覧表示時に指定する画像パスとしても“photoName”を利用することを想定しています。


上記構造のxmlを、simpleXMLを用いて呼出し、下記のようなレイアウトで一覧表示したいと考えております。(10件毎にページ分割)


<< < 1 2 3 4 5 ... 10 11 12 > >>

┏━━┓ ┏━━┓ ┏━━┓ ┏━━┓ ┏━━┓
┃画像┃ ┃画像┃ ┃画像┃ ┃画像┃ ┃画像┃
┗━━┛ ┗━━┛ ┗━━┛ ┗━━┛ ┗━━┛
投稿者名 投稿者名 投稿者名 投稿者名 投稿者名
コ メ ン ト コ メ ン ト コ メ ン ト コ メ ン ト コ メ ン ト

┏━━┓ ┏━━┓ ┏━━┓ ┏━━┓ ┏━━┓
┃画像┃ ┃画像┃ ┃画像┃ ┃画像┃ ┃画像┃
┗━━┛ ┗━━┛ ┗━━┛ ┗━━┛ ┗━━┛
投稿者名 投稿者名 投稿者名 投稿者名 投稿者名
コ メ ン ト コ メ ン ト コ メ ン ト コ メ ン ト コ メ ン ト

<< < 1 2 3 4 5 ... 10 11 12 > >>


simpleXMLを用いて出力するHTMLをCSSで上記のようなレイアウトに整形することを想定しておりますので、出力するHTMLは下記のような構造を想定しています。


  1. <div class="pageNav">ページ分割ナビゲーション</div>
  2. <div class="photo">投稿画像</div> <!--サムネイルの生成は別途関数で行います-->
  3. <div class="userName">投稿者名</div>
  4. <div class="comment">コメント</div>
  5. <div class="pageNav">ページ分割ナビゲーション</div>

※以上の内容は、恥ずかしながら“投稿内容の削除機能”を技術的な問題から保留とした上での仕様となります。

以上につき、simpleXML+ページ分割を実現する考え方、ノウハウが記載されている書籍の紹介だけでも構いません。あるいはPHPコード例や、アドバイス・ご教授など頂きたく、なにとぞご助言のほど宜しくお願い申し上げます。

この質問への意見の募集は締め切られ、ポイントは既に配分されました。
意見を投稿することはできますが、ポイントを受け取ることはできません。



ツリー一覧

┣A01NurseAngelとりあえず何故XMLで保存すると言いたいがまあいいや
┣A02magicflute2エラー処理もなく、洗練されてはいませんが…。 ※イン
┃┗A02-1zakobaご教授頂いた内容につき、幾つか理解出来ずにおりまし
┃ ┗A02-1-1magicflute2>>CODE <?php /** * 現在のページ番号を$_GETから
┃  ┣A02-1-1-1zakoba詳細なご教授ありがとうございます。 早速教えて頂い
┃  ┗A02-1-1-2zakobaご教授頂いてから色々試してみたのですが、★印の部分
┗A03yuu_a0>> 1, >> 2 の他は、 ・XSL (クライアント側でやら

回答一覧

並び替え:

A01 満足
answererNurseAngel [7月20日 01:58]

とりあえず何故XMLで保存すると言いたいがまあいいや。


SimpelXMLには"ポインタを11件目まで進める"みたいなメソッドはおそらくないと思われるので
取得部分はSimpleXMLElement::children()したあと
必要な部分までnext()なんかを空回ししないといけないんじゃないかと。


ページャーは単にSimpleXMLElement::count()で全件数を数えればいいだけです。
別に特別な考え方とかは必要ありません。

この意見に回答する

ツリーへ TOPへ

A02 満足
answerermagicflute2 [7月20日 02:35] (最終編集:7月20日 09:09)

エラー処理もなく、洗練されてはいませんが…。
※インデントされている部分は、while,if,for内を示しています。
※ページリンクは、とりあえずページ番号だけです。
  1. > 1ページの最大表示数を定義(1以上)
  2.  
  3. > 変数を0で初期化
  4.    ・現在のページ番号
  5.    ・dataの開始インデックス
  6.    ・ループカウンタ
  7.  
  8. > 現在のページ番号が、$_GETで取得できて、数値である場合の判定
  9.    ・ページ番号を$_GETから取得
  10.    ・dataの開始インデックスを算出(最大表示数とページ番号の乗算)
  11.  
  12. > XMLをロード
  13.  
  14. > 全data数を取得
  15. > ページ総数の算出。全data数を最大表示数で割り、小数点切上げ
  16.  
  17. > #1 外側のdiv要素を表示
  18.  
  19. > whileでループ
  20.    条件式は、isset(dataプロパティ[dataの開始インデックス])
  21.  
  22.   > ループカウンタが、最大表示数に達した場合の判定
  23.     > breakする
  24.  
  25.   > #2 float: leftを指定したdiv要素
  26.   > dataプロパティの内容を展開(htmlspecialcharsする)
  27.   > #2の閉じタグ
  28.  
  29.   > ループカウンタが0より大きく、(ループカウンタ+1)が5で割り切れるかの判定
  30.     > clear: both;を指定したbr要素を表示
  31.  
  32.   > dataの開始インデックスを++
  33.   > ループカウンタを++
  34.  
  35. > ループカウンタが5より小さい場合の判定
  36.   > clear: both;を指定したbr要素を表示
  37.  
  38. > #1の閉じタグを表示
  39.  
  40. > ページ数分、forでループ。初期値は1から加算
  41.  
  42. > a要素にページ番号を文字列連結し表示
--
余談:
・上記の例では、ページ番号は、0から始まる。
 1から始めたければ、$_GETで-し、a要素で+すればよい。
・xpathには複数の条件が指定できるので、取得範囲が指定できる。
  data[@num>0 and @num<10] 
・XMLのデータサイズは大きくなりがち、
  dataをループする際は、foreachより、開始インデックスを指定したwhileを

この意見に回答する

ツリーへ TOPへ

A02-1
replyerzakoba [7月23日 18:44] (最終編集:7月23日 18:48)

ご教授頂いた内容につき、幾つか理解出来ずにおりましたので、ご面倒をお掛け致しますが、下記の3点について追加質問させて頂きます。稚拙な内容かとは存じますが、何卒ご教授の程宜しくお願い申し上げます。

【1】
# > 現在のページ番号が、$_GETで取得できて、数値である場合の判定
#    ・ページ番号を$_GETから取得
#    ・dataの開始インデックスを算出(最大表示数とページ番号の乗算)

ページ番号を$_GETから取得というのは現在のページ番号を「$nPage」とした場合、
  1. $_GET['$nPage''];
ということになりますでしょうか?

また、dataの開始インデックスとは、こちらより提示させて頂いたxml上の要素である「data」を指しておりますでしょうか。「最大表示数×ページ番号=開始インデックス」という考え方を理解することが叶わず、開始インデックスとはどのように役立つ存在であるかをご教授頂けると助かります。


【2】
# > 全data数を取得
# > ページ総数の算出。全data数を最大表示数で割り、小数点切上げ

NurseAngel様からもご助言頂けているSimpleXMLElementのchildren()後にcount()で件数を取得するものと認識致しましたが、認識に相違ないでしょうか。

また、SimpleXMLElementの使用方法が分からずリファレンスなどを参考にしておりましたが、反復処理にて返り値を得ることは理解できたものの、具体的にどのようなコードで全件数を取得すべきか分からずにおりました。この点につきましてもコード例を教えて頂きたく、お願い申し上げます。


【3】
# > a要素にページ番号を文字列連結し表示

ページ番号を、$_GETで取得というくだりを理解することが出来なかった為、具体的なコードを考えることができませんでしたが、いずれにしても各ページへのリンクアドレス(href要素)をどのようにすべきかコードを考えることができませんでした。この点につきましてもコード例をお聞きしたく、なにとぞご教授の程、宜しくお願い申し上げます。

この意見に回答する

ツリーへ TOPへ

A02-1-1 満足
replyermagicflute2 [7月23日 20:06] (最終編集:7月23日 21:42)

  1. <?php
  2. /**
  3.  * 現在のページ番号を$_GETから取得、もしくは初期化
  4.  *
  5.  * GETでnPageパラメータが無い、0、数値以外の時、
  6.  * 同じ0ページ(最初のページ)を表示させる
  7.  * http://example.com/index.php
  8.  * http://example.com/index.php?nPage=0
  9.  * http://example.com/index.php?nPage=あいうえお
  10.  * dataの数よりも大きい値だった場合も、0ページを表示させてもいい
  11.  * http://example.com/index.php?nPage=99999
  12.  *
  13.  */
  14. $nPage = (isset($_GET['nPage']) && is_numeric($_GET['nPage']))?
  15.             $_GET['nPage'] : 0;
  16.  
  17.  
  18. /**
  19.  * dataとはdata要素の事
  20.  * しかし、提示されたものには、ルート要素が無いので、XMLの形をなしていない
  21.  * root要素をつけて、simplexmlでオブジェクトにしました。
  22.  */
  23. <root>
  24.   <data id="00001">
  25.   …
  26.   </data>
  27.   <data id="00002">
  28.   …
  29.   </data>
  30.   …
  31. </root>
  32.  
  33. $xmlobj = simplexml_load_***();
  34. $xmlobj->data;
  35.  
  36. /**
  37.  * 最大表示数×ページ番号=開始インデックス
  38.  * 開始インデックスとはどのように役立つ存在であるか
  39.  *
  40.  * (以下の説明では、data idは連番で抜けが無いとします)
  41.  */
  42. 最大表示数   :1ページに10
  43. ページ番号   :$_GETで取得した現在のページ番号(全dataの分割数)
  44. 開始インデックス:配列data[n]の添え字 n の事。data[10]なら2分割目を表す
  45. [data id:00001][data id:00010] 1分割目 or 0ページ目、開始位置:data[0]
  46. [data id:00011][data id:00020] 2分割目 or 1ページ目、開始位置:data[10]
  47. [data id:00021][data id:00030] 3分割目 or 2ページ目、開始位置:data[20]
  48. [data id:00091][data id:00094] 9分割目 or 8ページ目、開始位置:data[90]
  49.  
  50.  
  51. // dataの開始インデックスを算出(最大表示数とページ番号の乗算)
  52. $start_index = 10 * 0// 1分割目
  53. $start_index = 10 * 1// 2分割目
  54. $start_index = 10 * 2// 3分割目
  55.  
  56.  
  57. /**
  58.  * 全data数を取得
  59.  */
  60. $all_data_count = count($xmlobj->data);
  61. ($xmlobj->data->count()の書式は、php5.3.x以降でないと無理)
  62.  
  63. /**
  64.  * ページ総数の算出。全data数を最大表示数で割り、小数点切上げ
  65.  *
  66.  * $all_data_count / 10は、8.4の様に小数点がつく場合がある
  67.  * そこで、小数点を切り上げ、9とする
  68.  * ※10の部分は、ゼロ割になってはならない
  69.  */
  70. $total_page_count = ceil( $all_data_count / 10 )
  71.  
  72. /**
  73.  * SimpleXMLElementの使用方法が分からず
  74.  *
  75.  * ループは、扱いやすさ、速度面より、foreach ではなく、whileを使う事
  76.  */
  77. // まずは、xmlobjがどのような構造かを調べる
  78. // これにより、dataは配列という事が判る
  79. var_dump( $xmlobj );
  80.  
  81. // いま幾つデータを取得したかのカウンター。(上限10件)
  82. $counter = 0;
  83. while ( isset( $xmlobj->data[ $start_index ] ) ) {
  84.     // 10件に達したらループを抜ける
  85.     if ( $counter > 9 ) break;
  86.     // dataに含まれる各要素、id属性を取得
  87.     $datanode = $xmlobj->data[ $start_index ];
  88.     // レイアウトはCSSで
  89.     echo $datanode['id'];
  90.     echo $datanode->userName;
  91.     // カウンタ、開始インデックスを加算
  92.     $counter++;
  93.     $start_index++;
  94. }
  95.  
  96. /**
  97.  * a要素にページ番号を文字列連結し表示
  98.  *
  99.  * 結果
  100.  * <a href="?nPage=0">0 </a>
  101.  * <a href="?nPage=1">1 </a>
  102.  * <a href="?nPage=2">2 </a>
  103.  * …
  104.  * <a href="?nPage=8">8 </a>
  105.  * 0から始まっているので、全体が完成してから、1から開始するように変更する
  106.  * $i = 1; $i <= $total_page_count; $i++
  107.  * 他に、$_GET['nPage'] : 0; の0を1にする
  108.  * $start_index = 10 * 0; ここは、配列の添え字で使用されるので0からでいい
  109.  */
  110. for ( $i = 0$i < $total_page_count$i++ ) {
  111.     echo '<a href="?nPage='.$i.'">'.$i.'&nbsp;</a>'.PHP_EOL;
  112. }

この意見に回答する

ツリーへ TOPへ

A02-1-1-1
replyerzakoba [7月23日 20:31]

詳細なご教授ありがとうございます。
早速教えて頂いた内容を一行ずつ読み解きながら勉強させて頂きます。

これだけの長文、貴重なお時間を頂いたものと思います。
重ねて御礼申し上げます。

また改めてこちらに結果を報告させて頂く所存です。
まずは御礼まで申し上げます。

この意見に回答する

ツリーへ TOPへ

A02-1-1-2
replyerzakoba [9月29日 17:05] (最終編集:9月29日 17:06)

ご教授頂いてから色々試してみたのですが、★印の部分が解決できず、
誠に恥ずかしい限りですが、下記の★印箇所についてコード例を
ご教授頂きたく、お時間頂けるようでしたらご確認の程お願い申し上げます。



//1ページの最大表示数を定義(1以上)
$maxView=10;

//*********************************************************************
//変数を0で初期化
//*********************************************************************

//現在のページ番号
$nPage=0;

//dataの開始インデックス
$dataIndex=0;

//ループカウンタ
$counter = 0;

//*********************************************************************
//現在のページ番号が、$_GETで取得できて、数値である場合の判定
//*********************************************************************

//ページ番号を$_GETから取得
$nPage = (isset($_GET['nPage']) && is_numeric($_GET['nPage']))?
$_GET['nPage'] : 0;

//dataの開始インデックスを算出(最大表示数とページ番号の乗算)
$start_index = 10 * 0;

// XMLをロード
$xmlobj=simplexml_load_file("http://www.略.xml");
$xmlobj->data;

//全data数を取得
$all_data_count = count($xmlobj->data);

//ページ総数の算出。全data数を最大表示数で割り、小数点切上げ
$total_page_count = ceil( $all_data_count / 10 );

//#1 外側のdiv要素を表示
echo "<div class=¥"mainContainer¥">¥n";

//*********************************************************************
//whileでループ
//*********************************************************************
//条件式は、isset(dataプロパティ[dataの開始インデックス])
while( isset(data['$dataIndex']) ){

//ループカウンタが、最大表示数に達した場合の判定
while ( isset( $xmlobj->data[ $start_index ] ) ) { 
    // 10件に達したらループを抜ける 
    if ( $counter > 9 ) break; 
    // dataに含まれる各要素、id属性を取得 
    $datanode = $xmlobj->data[ $start_index ]; 
    // レイアウトはCSSで 
    echo $datanode['id']; 
    echo $datanode->userName; 
    // カウンタ、開始インデックスを加算 
    $counter++; 
    $start_index++; 
}

// #2 float: leftを指定したdiv要素
echo “<div class=¥"dataContainer¥">¥n";

//dataプロパティの内容を展開(htmlspecialcharsする)
//セキュリティに考慮しENT_QUOTESを第2引数に指定
★★★ 記述方法が不明な箇所 ★★★

//#2の閉じタグ  
echo "</div>¥n";

//ループカウンタが0より大きく、(ループカウンタ+1)が5で割り切れるかの判定
★★★ 記述方法が不明な箇所 ★★★

//clear: both;を指定したbr要素を表示
echo "<br class=¥"clear¥">¥n";

//dataの開始インデックスを++
$dataIndex = $dataIndex++;

//ループカウンタを++
$rCount = $rCount++;

//ループカウンタが5より小さい場合の判定
★★★ 記述方法が不明な箇所 ★★★

//clear: both;を指定したbr要素を表示
echo "<br class=¥"clear¥">¥n";

//#1の閉じタグを表示
echo "</div>¥n";

//ページ数分、forでループ。初期値は1から加算
//a要素にページ番号を文字列連結し表示
for ( $i = 0; $i < $total_page_count; $i++ ) { 
    echo '<a href="?nPage='.$i.'">'.$i.'&nbsp;</a>'.PHP_EOL; 
}

この意見に回答する

ツリーへ TOPへ

A03 満足
answereryuu_a0 [7月21日 05:06] (最終編集:7月21日 07:14)

>> 1, >> 2 の他は、

・XSL (クライアント側でやらせると多分重いから、サーバ側でやっといてキャッシュしとけばいい)
・xml やめて、xhtml にして xinclude + xpointer
・xml やめて、データベース

css page-break か、section(HTML5) あたり使えそうな気がするけど、どうなんだろ。

この意見に回答する

ツリーへ TOPへ

<<質問一覧へ



Pick Up Q&A

Q
PHPのHTML埋め込み記述について
 このエントリーをはてなブックマークに追加 
A
$_POST["data"] == "男" ? $val = "checked" : $val = "" ; の意味は以下と同じです。 if($_POST["data"] == "男"){ $val = "checked; } e...

>>続きを読む

kende様のご指摘通り、三項演算子を使用する際には、コードの複雑度などを考慮する必要がありますね。書きやすさと共に可読性も追求したいところですね。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)