たけるのプログラミング

作ったものとか、気ままにアップします。

【PHP】Pagination(ページネーション)を作ってみた!!!

今回は、長いコンテンツやページを分割する際に使われる「ページネーション」を作っていきたいと思います。

完成品

5データごとにページを変える

f:id:takeru232423:20210902130424p:plain

7をクリックすると
f:id:takeru232423:20210902130546p:plain

ソースコード

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="style.css" rel="stylesheet">
</head>

<body>
    <?php
//db接続処理
try {
    $db = new PDO('mysql:dbname=testing;host=localhost;charset=utf8', 'root', 'root');
} catch (PDOException $e) {
    echo 'DB接続エラーが起こりました。'. $e->getMessage();
}
if (isset($_GET['page'])) {
    $page=$_GET['page'];
    if ($page>=7) {
        //1〜6ページの時はページネーションが1〜10
        //7ページ目は2〜11になる → 7-5 と 7+4
        //8ページ目は3〜12になる → 8-5 と 8+4
        $page_first = $page - 5;
        $page_last = $page + 4;
    }
} else {
    $page=1;
}

//全データ数を取得
$count_data = $db->prepare("SELECT COUNT(*) FROM pagination");
$count_data->execute();
$count_data = $count_data->fetch(PDO::FETCH_COLUMN);

//5個ずつ表示する
$pagination=5;

//ページごとのデータのヘッドのインデックス
$page_per_data_head = $pagination*($page-1);

//必要なページ数を計算
$pages = ceil($count_data/$pagination);

//LIMIT0、10 の意味は 1番目のデータから10個データを取得する。
//0が1というのは配列のインデックスと同じイメージです。
$comments = $db->prepare("SELECT comment FROM pagination LIMIT ?,5");
$comments->bindParam(1, $page_per_data_head, PDO::PARAM_INT);
$comments->execute();
$comments_list = $comments->fetchAll(PDO::FETCH_ASSOC);

?>
    <!--コメント表示-->
    <div class="comment_area">
        <?php foreach ($comments_list as $comment) { ?>
        <p><?php echo $comment['comment'];  ?></p>
        <br>
        <?php } ?>
    </div>
    <!--10ページ以下だった場合は全て表示-->
    <div class="pagination_area">
        <?php if ($pages<=10) {?>
        <?php for ($i=1;$i<=$pages;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>

        <!--10ページより多かった、かつ現在6ページ以下の場合-->
        <?php } elseif ($pages>10&&$page<=6) {?>
        <?php for ($i=1;$i<=10;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>

        <!--10ページより多かった、かつ現在6ページより大きい場合-->
        <?php } elseif ($pages>10&&$page>6) {
    if ($page_last>=$pages) {
        $page_last = $pages;
    } ?>
        <?php for ($i=$page_first;$i<=$page_last;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>
        <?php
} ?>
    </div>

</body>

</html>

コードの説明

データベースの接続
//db接続処理
try {
    $db = new PDO('mysql:dbname=testing;host=localhost;charset=utf8', 'root', 'root');
} catch (PDOException $e) {
    echo 'DB接続エラーが起こりました。'. $e->getMessage();
}
テーブルのデータ数を取得
//全データ数を取得
$count_data = $db->prepare("SELECT COUNT(*) FROM pagination");
$count_data->execute();
$count_data = $count_data->fetch(PDO::FETCH_COLUMN);

SQLのCOUNT(*)で行数をカウントして、fetchして、COUNT(*)カラムの最初の行の値を取得して、その値を$count_dataに入れる。

1ページに表示するデータ数を指定
//5個ずつ表示する
$pagination=5;

$paginationに代入する。

必要なページ数を計算する
$pages = ceil($count_data/$pagination);

先ほど計算したデータ数である$count_dataを、$paginationで割り、必要なページ数を計算し、その値を$pagesに入れる。例えば4.1だった場合、5ページ必要なのでceil()関数を使い切り上げを行う。

5個データを抽出する
$comments = $db->prepare("SELECT comment FROM pagination LIMIT ?,5");
$comments->bindParam(1, $page_per_data_head, PDO::PARAM_INT);
$comments->execute();
$comments_list = $comments->fetchAll(PDO::FETCH_ASSOC);

SQLのLIMITについて、プログラミングにおける配列をイメージする。例えば1個目のデータから5つデータを抽出する場合 " LIMIT 0,5 "と 指定する。つまり1ページ目のデータはLIMIT 0,5と指定し、2ページ目のデータはLIMIT5,5、3ページ目のデータはLIMIT10,5とSQLを指定する。つまり第二引数は5固定で第一引数は以下のコードのように指定する。

$page_per_data_head = $pagination*($page-1);

5データずつページを区切るので、$paginationの値は5、に現在いるページ数から1引いたものをかけることによって第一引数を求めることができる。

ページネーションのリンクの設定
if (isset($_GET['page'])) {
    $page=$_GET['page'];
    if ($page>=7) {
        //1〜6ページの時はページネーションが1〜10
        //7ページ目は2〜11になる → 7-5 と 7+4
        //8ページ目は3〜12になる → 8-5 と 8+4
        $page_first = $page - 5;
        $page_last = $page + 4;
    }
} else {
    $page=1;
}

URLのパラメータpageの値を$pageに代入する。

もし7ページだった場合、

7-5=2 3 4 5 6 7 8 9 10 11=7+4

もし8ページだった場合、

8-5=3 4 5 6 7 8 9 10 11 12=8+4

になるようにするため、数列の最初と最後の値を$page_first、$page_lastに代入する。

ページネーションのリンク表示①
  <?php if ($pages<=10) {?>
        <?php for ($i=1;$i<=$pages;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>

        <!--10ページより多かった、かつ現在6ページ以下の場合-->
        <?php } elseif ($pages>10&&$page<=6) {?>
        <?php for ($i=1;$i<=10;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>
ページネーションのリンク表示②
 <?php } elseif ($pages>10&&$page>6) {
    if ($page_last>=$pages) {
        $page_last = $pages;
    } ?>
        <?php for ($i=$page_first;$i<=$page_last;$i++) { ?>
        <a href="http://localhost:8888/pagination/index.php?page=<?php echo $i ?>"><?php echo $i ?></a>
        <?php } ?>
        <?php
} ?>

$page_lastを$page + 4で求めましたが、$pageの数によっては必要なページ数である$pagesを超える場合があります。
そのため

  if ($page_last>=$pages) {
        $page_last = $pages;
    } ?>

を記述し、$page_lastが$pagesを超えた場合に、$page_lastを$pagesの値にします。こうすることで無限にページネーションのリンクが出現することを防ぎます。

最後に

競技プログラミングぽくアルゴリズム考えるの面白かったです。

何かご指摘等ございましたら、コメントいただけたらありがたいです。